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, &mac_addr) >= 0);
71 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_CLIENTID) == -EINVAL);
72 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EEXIST);
73 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_NTP_SERVER) == -EEXIST);
74 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == 0);
75 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
76 assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL);
78 assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
80 assert_se(sd_dhcp6_client_detach_event(client) >= 0);
81 assert_se(!sd_dhcp6_client_unref(client));
86 static int test_option(sd_event *e) {
89 0x00, DHCP6_OPTION_ORO, 0x00, 0x07,
90 'A', 'B', 'C', 'D', 'E', 'F', 'G',
91 0x00, DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
92 '1', '2', '3', '4', '5', '6', '7', '8', '9',
97 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 uint8_t *optval, *buf, *out;
105 size_t zero = 0, pos = 3;
106 size_t buflen = sizeof(packet), outlen = sizeof(result);
109 printf("* %s\n", __FUNCTION__);
111 assert_se(buflen == outlen);
113 assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen,
114 &optval) == -ENOMSG);
121 assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
124 assert_se(buf == &packet[pos]);
125 assert_se(optcode == DHCP6_OPTION_ORO);
126 assert_se(optlen == 7);
127 assert_se(buflen + pos == sizeof(packet));
129 assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
131 assert_se(out == &result[pos]);
132 assert_se(*out == 0x00);
134 assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
137 assert_se(buf == &packet[pos]);
138 assert_se(optcode == DHCP6_OPTION_VENDOR_CLASS);
139 assert_se(optlen == 9);
140 assert_se(buflen + pos == sizeof(packet));
142 assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
144 assert_se(out == &result[pos]);
145 assert_se(*out == 'B');
147 assert_se(memcmp(packet, result, sizeof(packet)) == 0);
152 static uint8_t msg_advertise[198] = {
153 0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e,
154 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30,
155 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03,
156 0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00,
157 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05,
158 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
159 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c,
160 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
161 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00,
162 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28,
163 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65,
164 0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65,
165 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66,
166 0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e,
167 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68,
168 0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8,
169 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00,
170 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b,
171 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
172 0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20,
173 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
175 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19,
176 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d,
177 0x53, 0x00, 0x07, 0x00, 0x01, 0x00
180 static uint8_t msg_reply[173] = {
181 0x07, 0xf7, 0x4e, 0x57, 0x00, 0x02, 0x00, 0x0e,
182 0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53,
183 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, 0x00, 0x01,
184 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b,
185 0xf3, 0x30, 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d,
186 0x00, 0x03, 0x00, 0x4a, 0x0e, 0xcf, 0xa3, 0x7d,
187 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78,
188 0x00, 0x05, 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8,
189 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3,
190 0x09, 0x3c, 0x55, 0xad, 0x00, 0x00, 0x00, 0x96,
191 0x00, 0x00, 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x1e,
192 0x00, 0x00, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64,
193 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20,
194 0x77, 0x65, 0x72, 0x65, 0x20, 0x61, 0x73, 0x73,
195 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x00, 0x17,
196 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
197 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
198 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, 0x03, 0x6c,
199 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61,
200 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, 0x01, 0x0d,
201 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00,
202 0x00, 0x00, 0x00, 0x00, 0x01
205 static int test_advertise_option(sd_event *e) {
206 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
207 DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
208 uint8_t *optval, *opt = msg_advertise + sizeof(DHCP6Message);
210 size_t optlen, len = sizeof(msg_advertise) - sizeof(DHCP6Message);
212 uint8_t preference = 255;
213 struct in6_addr addr;
214 uint32_t lt_pref, lt_valid;
216 bool opt_clientid = false;
219 printf("* %s\n", __FUNCTION__);
221 assert_se(dhcp6_lease_new(&lease) >= 0);
223 assert_se(advertise->type == DHCP6_ADVERTISE);
224 assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) ==
227 while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen,
231 case DHCP6_OPTION_CLIENTID:
232 assert_se(optlen == 14);
237 case DHCP6_OPTION_IA_NA:
238 assert_se(optlen == 94);
239 assert_se(!memcmp(optval, &msg_advertise[26], optlen));
241 val = htobe32(0x0ecfa37d);
242 assert_se(!memcmp(optval, &val, sizeof(val)));
245 assert_se(!memcmp(optval + 4, &val, sizeof(val)));
248 assert_se(!memcmp(optval + 8, &val, sizeof(val)));
250 assert_se(dhcp6_option_parse_ia(&optval, &optlen,
256 case DHCP6_OPTION_SERVERID:
257 assert_se(optlen == 14);
258 assert_se(!memcmp(optval, &msg_advertise[179], optlen));
260 assert_se(dhcp6_lease_set_serverid(lease, optval,
264 case DHCP6_OPTION_PREFERENCE:
265 assert_se(optlen == 1);
268 assert_se(dhcp6_lease_set_preference(lease,
272 case DHCP6_OPTION_ELAPSED_TIME:
273 assert_se(optlen == 2);
283 assert_se(r == -ENOMSG);
285 assert_se(opt_clientid);
287 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
289 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
290 assert_se(lt_pref == 150);
291 assert_se(lt_valid == 180);
292 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
293 <_valid) == -ENOMSG);
295 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
297 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
298 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
299 <_valid) == -ENOMSG);
300 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
301 <_valid) == -ENOMSG);
302 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
304 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
305 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
306 <_valid) == -ENOMSG);
308 assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0);
309 assert_se(len == 14);
310 assert_se(!memcmp(opt, &msg_advertise[179], len));
312 assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
313 assert_se(preference == 0);
318 static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
319 assert_not_reached("Test case should have completed in 2 seconds");
324 int detect_vm(const char **id) {
328 int detect_container(const char **id) {
332 int detect_virtualization(const char **id) {
336 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
337 assert_se(index == test_index);
339 if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
342 return test_dhcp_fd[0];
345 static int test_client_send_reply(DHCP6Message *request) {
348 reply.transaction_id = request->transaction_id;
349 reply.type = DHCP6_REPLY;
351 memcpy(msg_reply, &reply.transaction_id, 4);
353 memcpy(&msg_reply[26], test_duid, sizeof(test_duid));
355 memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid));
357 assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply))
358 == sizeof(msg_reply));
363 static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
365 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
369 bool found_clientid = false, found_iana = false, found_serverid = false,
370 found_elapsed_time = false;
372 struct in6_addr addr;
374 uint32_t lt_pref, lt_valid;
376 assert_se(request->type == DHCP6_REQUEST);
378 assert_se(dhcp6_lease_new(&lease) >= 0);
380 while ((r = dhcp6_option_parse(&option, &len,
381 &optcode, &optlen, &optval)) >= 0) {
383 case DHCP6_OPTION_CLIENTID:
384 assert_se(!found_clientid);
385 found_clientid = true;
387 assert_se(!memcmp(optval, &test_duid,
392 case DHCP6_OPTION_IA_NA:
393 assert_se(!found_iana);
397 assert_se(optlen == 40);
398 assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid)));
401 assert_se(!memcmp(optval + 4, &val, sizeof(val)));
404 assert_se(!memcmp(optval + 8, &val, sizeof(val)));
406 assert_se(!dhcp6_option_parse_ia(&optval, &optlen,
407 optcode, &lease->ia));
411 case DHCP6_OPTION_SERVERID:
412 assert_se(!found_serverid);
413 found_serverid = true;
415 assert_se(optlen == 14);
416 assert_se(!memcmp(&msg_advertise[179], optval, optlen));
420 case DHCP6_OPTION_ELAPSED_TIME:
421 assert_se(!found_elapsed_time);
422 found_elapsed_time = true;
424 assert_se(optlen == 2);
430 assert_se(r == -ENOMSG);
431 assert_se(found_clientid && found_iana && found_serverid &&
434 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
436 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
437 assert_se(lt_pref == 150);
438 assert_se(lt_valid == 180);
440 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
441 <_valid) == -ENOMSG);
446 static int test_client_send_advertise(DHCP6Message *solicit)
448 DHCP6Message advertise;
450 advertise.transaction_id = solicit->transaction_id;
451 advertise.type = DHCP6_ADVERTISE;
453 memcpy(msg_advertise, &advertise.transaction_id, 4);
455 memcpy(&msg_advertise[8], test_duid, sizeof(test_duid));
457 memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid));
459 assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise))
460 == sizeof(msg_advertise));
465 static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
470 bool found_clientid = false, found_iana = false,
471 found_elapsed_time = false;
474 assert_se(solicit->type == DHCP6_SOLICIT);
476 while ((r = dhcp6_option_parse(&option, &len,
477 &optcode, &optlen, &optval)) >= 0) {
479 case DHCP6_OPTION_CLIENTID:
480 assert_se(!found_clientid);
481 found_clientid = true;
483 assert_se(optlen == sizeof(test_duid));
484 memcpy(&test_duid, optval, sizeof(test_duid));
488 case DHCP6_OPTION_IA_NA:
489 assert_se(!found_iana);
492 assert_se(optlen == 12);
494 memcpy(&test_iaid, optval, sizeof(test_iaid));
498 case DHCP6_OPTION_ELAPSED_TIME:
499 assert_se(!found_elapsed_time);
500 found_elapsed_time = true;
502 assert_se(optlen == 2);
508 assert_se(r == -ENOMSG);
509 assert_se(found_clientid && found_iana && found_elapsed_time);
514 int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
515 const void *packet, size_t len) {
516 struct in6_addr mcast =
517 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
518 DHCP6Message *message;
521 assert_se(s == test_dhcp_fd[0]);
522 assert_se(server_address);
524 assert_se(len > sizeof(DHCP6Message) + 4);
526 assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
528 message = (DHCP6Message *)packet;
529 option = (uint8_t *)(message + 1);
530 len -= sizeof(DHCP6Message);
532 assert_se(message->transaction_id & 0x00ffffff);
534 if (test_client_message_num == 0) {
535 test_client_verify_solicit(message, option, len);
536 test_client_send_advertise(message);
537 test_client_message_num++;
538 } else if (test_client_message_num == 1) {
539 test_client_verify_request(message, option, len);
540 test_client_send_reply(message);
541 test_client_message_num++;
547 static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
549 sd_event *e = userdata;
552 assert_se(event == DHCP6_EVENT_IP_ACQUIRE);
554 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
557 printf(" got DHCPv6 event %d\n", event);
562 static int test_client_solicit(sd_event *e) {
563 sd_dhcp6_client *client;
564 usec_t time_now = now(clock_boottime_or_monotonic());
567 printf("* %s\n", __FUNCTION__);
569 assert_se(sd_dhcp6_client_new(&client) >= 0);
572 assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
574 assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
575 assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
577 assert_se(sd_dhcp6_client_set_callback(client,
578 test_client_solicit_cb, e) >= 0);
580 assert_se(sd_event_add_time(e, &hangcheck, clock_boottime_or_monotonic(),
581 time_now + 2 * USEC_PER_SEC, 0,
582 test_hangcheck, NULL) >= 0);
584 assert_se(sd_dhcp6_client_start(client) >= 0);
588 hangcheck = sd_event_source_unref(hangcheck);
590 assert_se(!sd_dhcp6_client_unref(client));
592 test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
597 int main(int argc, char *argv[]) {
598 _cleanup_event_unref_ sd_event *e;
600 assert_se(sd_event_new(&e) >= 0);
602 log_set_max_level(LOG_DEBUG);
603 log_parse_environment();
606 test_client_basic(e);
608 test_advertise_option(e);
609 test_client_solicit(e);
611 assert_se(!sd_event_unref(e));