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_callback(client, NULL, NULL) >= 0);
73 assert_se(sd_dhcp6_client_detach_event(client) >= 0);
74 assert_se(!sd_dhcp6_client_unref(client));
79 static int test_option(sd_event *e) {
82 0x00, DHCP6_OPTION_ORO, 0x00, 0x07,
83 'A', 'B', 'C', 'D', 'E', 'F', 'G',
84 0x00, DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
85 '1', '2', '3', '4', '5', '6', '7', '8', '9',
90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 uint8_t *optval, *buf, *out;
98 size_t zero = 0, pos = 3;
99 size_t buflen = sizeof(packet), outlen = sizeof(result);
102 printf("* %s\n", __FUNCTION__);
104 assert_se(buflen == outlen);
106 assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen,
107 &optval) == -ENOMSG);
114 assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
117 assert_se(buf == &packet[pos]);
118 assert_se(optcode == DHCP6_OPTION_ORO);
119 assert_se(optlen == 7);
120 assert_se(buflen + pos == sizeof(packet));
122 assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
124 assert_se(out == &result[pos]);
125 assert_se(*out == 0x00);
127 assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
130 assert_se(buf == &packet[pos]);
131 assert_se(optcode == DHCP6_OPTION_VENDOR_CLASS);
132 assert_se(optlen == 9);
133 assert_se(buflen + pos == sizeof(packet));
135 assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
137 assert_se(out == &result[pos]);
138 assert_se(*out == 'B');
140 assert_se(memcmp(packet, result, sizeof(packet)) == 0);
145 static uint8_t msg_advertise[198] = {
146 0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e,
147 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30,
148 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03,
149 0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00,
150 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05,
151 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
152 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c,
153 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
154 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00,
155 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28,
156 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65,
157 0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65,
158 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66,
159 0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e,
160 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68,
161 0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8,
162 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00,
163 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b,
164 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
165 0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20,
166 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00,
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
168 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19,
169 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d,
170 0x53, 0x00, 0x07, 0x00, 0x01, 0x00
173 static uint8_t msg_reply[173] = {
174 0x07, 0xf7, 0x4e, 0x57, 0x00, 0x02, 0x00, 0x0e,
175 0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53,
176 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, 0x00, 0x01,
177 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b,
178 0xf3, 0x30, 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d,
179 0x00, 0x03, 0x00, 0x4a, 0x0e, 0xcf, 0xa3, 0x7d,
180 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78,
181 0x00, 0x05, 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8,
182 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3,
183 0x09, 0x3c, 0x55, 0xad, 0x00, 0x00, 0x00, 0x96,
184 0x00, 0x00, 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x1e,
185 0x00, 0x00, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64,
186 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20,
187 0x77, 0x65, 0x72, 0x65, 0x20, 0x61, 0x73, 0x73,
188 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x00, 0x17,
189 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
190 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
191 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, 0x03, 0x6c,
192 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61,
193 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, 0x01, 0x0d,
194 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00,
195 0x00, 0x00, 0x00, 0x00, 0x01
198 static int test_advertise_option(sd_event *e) {
199 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
200 DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
201 uint8_t *optval, *opt = &msg_advertise[sizeof(DHCP6Message)];
203 size_t optlen, len = sizeof(msg_advertise);
205 uint8_t preference = 255;
206 struct in6_addr addr;
207 uint32_t lt_pref, lt_valid;
209 bool opt_clientid = false;
212 printf("* %s\n", __FUNCTION__);
214 assert_se(dhcp6_lease_new(&lease) >= 0);
216 assert_se(advertise->type == DHCP6_ADVERTISE);
217 assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) ==
220 while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen,
224 case DHCP6_OPTION_CLIENTID:
225 assert_se(optlen == 14);
230 case DHCP6_OPTION_IA_NA:
231 assert_se(optlen == 94);
232 assert_se(!memcmp(optval, &msg_advertise[26], optlen));
234 val = htobe32(0x0ecfa37d);
235 assert_se(!memcmp(optval, &val, sizeof(val)));
238 assert_se(!memcmp(optval + 4, &val, sizeof(val)));
241 assert_se(!memcmp(optval + 8, &val, sizeof(val)));
243 assert_se(dhcp6_option_parse_ia(&optval, &optlen,
249 case DHCP6_OPTION_SERVERID:
250 assert_se(optlen == 14);
251 assert_se(!memcmp(optval, &msg_advertise[179], optlen));
253 assert_se(dhcp6_lease_set_serverid(lease, optval,
257 case DHCP6_OPTION_PREFERENCE:
258 assert_se(optlen == 1);
261 assert_se(dhcp6_lease_set_preference(lease,
271 assert_se(r == -ENOMSG);
273 assert_se(opt_clientid);
275 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
277 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
278 assert_se(lt_pref == 150);
279 assert_se(lt_valid == 180);
280 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
281 <_valid) == -ENOMSG);
283 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
285 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
286 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
287 <_valid) == -ENOMSG);
288 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
289 <_valid) == -ENOMSG);
290 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
292 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
293 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
294 <_valid) == -ENOMSG);
296 assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0);
297 assert_se(len == 14);
298 assert_se(!memcmp(opt, &msg_advertise[179], len));
300 assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
301 assert_se(preference == 0);
306 static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
307 assert_not_reached("Test case should have completed in 2 seconds");
312 int detect_vm(const char **id) {
316 int detect_container(const char **id) {
320 int detect_virtualization(const char **id) {
324 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
325 assert_se(index == test_index);
327 if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
330 return test_dhcp_fd[0];
333 static int test_client_send_reply(DHCP6Message *request) {
336 reply.transaction_id = request->transaction_id;
337 reply.type = DHCP6_REPLY;
339 memcpy(msg_reply, &reply.transaction_id, 4);
341 memcpy(&msg_reply[26], test_duid, sizeof(test_duid));
343 memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid));
345 assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply))
346 == sizeof(msg_reply));
351 static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
353 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
357 bool found_clientid = false, found_iana = false, found_serverid = false;
359 struct in6_addr addr;
361 uint32_t lt_pref, lt_valid;
363 assert_se(request->type == DHCP6_REQUEST);
365 assert_se(dhcp6_lease_new(&lease) >= 0);
367 while ((r = dhcp6_option_parse(&option, &len,
368 &optcode, &optlen, &optval)) >= 0) {
370 case DHCP6_OPTION_CLIENTID:
371 assert_se(!found_clientid);
372 found_clientid = true;
374 assert_se(!memcmp(optval, &test_duid,
379 case DHCP6_OPTION_IA_NA:
380 assert_se(!found_iana);
384 assert_se(optlen == 40);
385 assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid)));
388 assert_se(!memcmp(optval + 4, &val, sizeof(val)));
391 assert_se(!memcmp(optval + 8, &val, sizeof(val)));
393 assert_se(!dhcp6_option_parse_ia(&optval, &optlen,
394 optcode, &lease->ia));
398 case DHCP6_OPTION_SERVERID:
399 assert_se(!found_serverid);
400 found_serverid = true;
402 assert_se(optlen == 14);
403 assert_se(!memcmp(&msg_advertise[179], optval, optlen));
409 assert_se(r == -ENOMSG);
410 assert_se(found_clientid && found_iana && found_serverid);
412 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
414 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
415 assert_se(lt_pref == 150);
416 assert_se(lt_valid == 180);
418 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
419 <_valid) == -ENOMSG);
424 static int test_client_send_advertise(DHCP6Message *solicit)
426 DHCP6Message advertise;
428 advertise.transaction_id = solicit->transaction_id;
429 advertise.type = DHCP6_ADVERTISE;
431 memcpy(msg_advertise, &advertise.transaction_id, 4);
433 memcpy(&msg_advertise[8], test_duid, sizeof(test_duid));
435 memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid));
437 assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise))
438 == sizeof(msg_advertise));
443 static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
448 bool found_clientid = false, found_iana = false;
451 assert_se(solicit->type == DHCP6_SOLICIT);
453 while ((r = dhcp6_option_parse(&option, &len,
454 &optcode, &optlen, &optval)) >= 0) {
456 case DHCP6_OPTION_CLIENTID:
457 assert_se(!found_clientid);
458 found_clientid = true;
460 assert_se(optlen == sizeof(test_duid));
461 memcpy(&test_duid, optval, sizeof(test_duid));
465 case DHCP6_OPTION_IA_NA:
466 assert_se(!found_iana);
469 assert_se(optlen == 12);
471 memcpy(&test_iaid, optval, sizeof(test_iaid));
477 assert_se(r == -ENOMSG);
478 assert_se(found_clientid && found_iana);
483 int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
484 const void *packet, size_t len) {
485 struct in6_addr mcast =
486 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
487 DHCP6Message *message;
490 assert_se(s == test_dhcp_fd[0]);
491 assert_se(server_address);
493 assert_se(len > sizeof(DHCP6Message) + 4);
495 assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
497 message = (DHCP6Message *)packet;
498 option = (uint8_t *)(message + 1);
499 len -= sizeof(DHCP6Message);
501 assert_se(message->transaction_id & 0x00ffffff);
503 if (test_client_message_num == 0) {
504 test_client_verify_solicit(message, option, len);
505 test_client_send_advertise(message);
506 test_client_message_num++;
507 } else if (test_client_message_num == 1) {
508 test_client_verify_request(message, option, len);
509 test_client_send_reply(message);
510 test_client_message_num++;
516 static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
518 sd_event *e = userdata;
521 assert_se(event == DHCP6_EVENT_IP_ACQUIRE);
524 printf(" got DHCPv6 event %d\n", event);
529 static int test_client_solicit(sd_event *e) {
530 sd_dhcp6_client *client;
531 usec_t time_now = now(CLOCK_MONOTONIC);
534 printf("* %s\n", __FUNCTION__);
536 assert_se(sd_dhcp6_client_new(&client) >= 0);
539 assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
541 assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
542 assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
544 assert_se(sd_dhcp6_client_set_callback(client,
545 test_client_solicit_cb, e) >= 0);
547 assert_se(sd_event_add_time(e, &hangcheck, CLOCK_MONOTONIC,
548 time_now + 2 * USEC_PER_SEC, 0,
549 test_hangcheck, NULL) >= 0);
551 assert_se(sd_dhcp6_client_start(client) >= 0);
555 hangcheck = sd_event_source_unref(hangcheck);
557 assert_se(!sd_dhcp6_client_unref(client));
559 test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
564 int main(int argc, char *argv[]) {
565 _cleanup_event_unref_ sd_event *e;
567 assert_se(sd_event_new(&e) >= 0);
569 log_set_max_level(LOG_DEBUG);
570 log_parse_environment();
573 test_client_basic(e);
575 test_advertise_option(e);
576 test_client_solicit(e);
578 assert_se(!sd_event_unref(e));