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 sd_dhcp6_lease_reset_address_iter(lease);
290 assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref,
292 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
293 assert_se(lt_pref == 150);
294 assert_se(lt_valid == 180);
295 assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref,
296 <_valid) == -ENOMSG);
298 sd_dhcp6_lease_reset_address_iter(lease);
299 assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref,
301 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
302 assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref,
303 <_valid) == -ENOMSG);
304 sd_dhcp6_lease_reset_address_iter(lease);
305 assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref,
307 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
308 assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref,
309 <_valid) == -ENOMSG);
311 assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0);
312 assert_se(len == 14);
313 assert_se(!memcmp(opt, &msg_advertise[179], len));
315 assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
316 assert_se(preference == 0);
321 static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
322 assert_not_reached("Test case should have completed in 2 seconds");
327 int detect_vm(const char **id) {
331 int detect_container(const char **id) {
335 int detect_virtualization(const char **id) {
339 static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
341 sd_event *e = userdata;
344 assert_se(event == DHCP6_EVENT_IP_ACQUIRE);
346 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
349 printf(" got DHCPv6 event %d\n", event);
354 static int test_client_send_reply(DHCP6Message *request) {
357 reply.transaction_id = request->transaction_id;
358 reply.type = DHCP6_REPLY;
360 memcpy(msg_reply, &reply.transaction_id, 4);
362 memcpy(&msg_reply[26], test_duid, sizeof(test_duid));
364 memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid));
366 assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply))
367 == sizeof(msg_reply));
372 static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
374 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
378 bool found_clientid = false, found_iana = false, found_serverid = false,
379 found_elapsed_time = false;
381 struct in6_addr addr;
383 uint32_t lt_pref, lt_valid;
385 assert_se(request->type == DHCP6_REQUEST);
387 assert_se(dhcp6_lease_new(&lease) >= 0);
389 while ((r = dhcp6_option_parse(&option, &len,
390 &optcode, &optlen, &optval)) >= 0) {
392 case DHCP6_OPTION_CLIENTID:
393 assert_se(!found_clientid);
394 found_clientid = true;
396 assert_se(!memcmp(optval, &test_duid,
401 case DHCP6_OPTION_IA_NA:
402 assert_se(!found_iana);
406 assert_se(optlen == 40);
407 assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid)));
410 assert_se(!memcmp(optval + 4, &val, sizeof(val)));
413 assert_se(!memcmp(optval + 8, &val, sizeof(val)));
415 assert_se(!dhcp6_option_parse_ia(&optval, &optlen,
416 optcode, &lease->ia));
420 case DHCP6_OPTION_SERVERID:
421 assert_se(!found_serverid);
422 found_serverid = true;
424 assert_se(optlen == 14);
425 assert_se(!memcmp(&msg_advertise[179], optval, optlen));
429 case DHCP6_OPTION_ELAPSED_TIME:
430 assert_se(!found_elapsed_time);
431 found_elapsed_time = true;
433 assert_se(optlen == 2);
439 assert_se(r == -ENOMSG);
440 assert_se(found_clientid && found_iana && found_serverid &&
443 sd_dhcp6_lease_reset_address_iter(lease);
444 assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref,
446 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
447 assert_se(lt_pref == 150);
448 assert_se(lt_valid == 180);
450 assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref,
451 <_valid) == -ENOMSG);
456 static int test_client_send_advertise(DHCP6Message *solicit)
458 DHCP6Message advertise;
460 advertise.transaction_id = solicit->transaction_id;
461 advertise.type = DHCP6_ADVERTISE;
463 memcpy(msg_advertise, &advertise.transaction_id, 4);
465 memcpy(&msg_advertise[8], test_duid, sizeof(test_duid));
467 memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid));
469 assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise))
470 == sizeof(msg_advertise));
475 static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
480 bool found_clientid = false, found_iana = false,
481 found_elapsed_time = false;
484 assert_se(solicit->type == DHCP6_SOLICIT);
486 while ((r = dhcp6_option_parse(&option, &len,
487 &optcode, &optlen, &optval)) >= 0) {
489 case DHCP6_OPTION_CLIENTID:
490 assert_se(!found_clientid);
491 found_clientid = true;
493 assert_se(optlen == sizeof(test_duid));
494 memcpy(&test_duid, optval, sizeof(test_duid));
498 case DHCP6_OPTION_IA_NA:
499 assert_se(!found_iana);
502 assert_se(optlen == 12);
504 memcpy(&test_iaid, optval, sizeof(test_iaid));
508 case DHCP6_OPTION_ELAPSED_TIME:
509 assert_se(!found_elapsed_time);
510 found_elapsed_time = true;
512 assert_se(optlen == 2);
518 assert_se(r == -ENOMSG);
519 assert_se(found_clientid && found_iana && found_elapsed_time);
524 static void test_client_information_cb(sd_dhcp6_client *client, int event,
526 sd_event *e = userdata;
529 assert_se(event == DHCP6_EVENT_INFORMATION_REQUEST);
532 printf(" got DHCPv6 event %d\n", event);
534 assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0);
535 assert_se(sd_dhcp6_client_set_callback(client,
536 test_client_solicit_cb, e) >= 0);
538 assert_se(sd_dhcp6_client_start(client) >= 0);
541 static int test_client_verify_information_request(DHCP6Message *information_request,
542 uint8_t *option, size_t len) {
544 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
548 bool found_clientid = false, found_elapsed_time = false;
550 struct in6_addr addr;
551 uint32_t lt_pref, lt_valid;
553 assert_se(information_request->type == DHCP6_INFORMATION_REQUEST);
555 assert_se(dhcp6_lease_new(&lease) >= 0);
557 while ((r = dhcp6_option_parse(&option, &len,
558 &optcode, &optlen, &optval)) >= 0) {
560 case DHCP6_OPTION_CLIENTID:
561 assert_se(!found_clientid);
562 found_clientid = true;
564 assert_se(optlen == sizeof(test_duid));
565 memcpy(&test_duid, optval, sizeof(test_duid));
569 case DHCP6_OPTION_IA_NA:
570 assert_not_reached("IA TA option must not be present");
574 case DHCP6_OPTION_SERVERID:
575 assert_not_reached("Server ID option must not be present");
579 case DHCP6_OPTION_ELAPSED_TIME:
580 assert_se(!found_elapsed_time);
581 found_elapsed_time = true;
583 assert_se(optlen == 2);
589 assert_se(r == -ENOMSG);
590 assert_se(found_clientid && found_elapsed_time);
592 sd_dhcp6_lease_reset_address_iter(lease);
594 assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref,
595 <_valid) == -ENOMSG);
600 int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
601 const void *packet, size_t len) {
602 struct in6_addr mcast =
603 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
604 DHCP6Message *message;
607 assert_se(s == test_dhcp_fd[0]);
608 assert_se(server_address);
610 assert_se(len > sizeof(DHCP6Message) + 4);
612 assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
614 message = (DHCP6Message *)packet;
615 option = (uint8_t *)(message + 1);
616 len -= sizeof(DHCP6Message);
618 assert_se(message->transaction_id & 0x00ffffff);
620 if (test_client_message_num == 0) {
621 test_client_verify_information_request(message, option, len);
622 test_client_send_reply(message);
623 test_client_message_num++;
624 } else if (test_client_message_num == 1) {
625 test_client_verify_solicit(message, option, len);
626 test_client_send_advertise(message);
627 test_client_message_num++;
628 } else if (test_client_message_num == 2) {
629 test_client_verify_request(message, option, len);
630 test_client_send_reply(message);
631 test_client_message_num++;
637 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
638 assert_se(index == test_index);
640 if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
643 return test_dhcp_fd[0];
646 static int test_client_solicit(sd_event *e) {
647 sd_dhcp6_client *client;
648 usec_t time_now = now(clock_boottime_or_monotonic());
652 printf("* %s\n", __FUNCTION__);
654 assert_se(sd_dhcp6_client_new(&client) >= 0);
657 assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
659 assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
660 assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
664 assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
665 assert_se(val == false);
666 assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0);
667 assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
668 assert_se(val == true);
670 assert_se(sd_dhcp6_client_set_callback(client,
671 test_client_information_cb, e) >= 0);
673 assert_se(sd_event_add_time(e, &hangcheck, clock_boottime_or_monotonic(),
674 time_now + 2 * USEC_PER_SEC, 0,
675 test_hangcheck, NULL) >= 0);
677 assert_se(sd_dhcp6_client_start(client) >= 0);
681 hangcheck = sd_event_source_unref(hangcheck);
683 assert_se(!sd_dhcp6_client_unref(client));
685 test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
690 int main(int argc, char *argv[]) {
691 _cleanup_event_unref_ sd_event *e;
693 assert_se(sd_event_new(&e) >= 0);
695 log_set_max_level(LOG_DEBUG);
696 log_parse_environment();
699 test_client_basic(e);
701 test_advertise_option(e);
702 test_client_solicit(e);
704 assert_se(!sd_event_unref(e));