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 sd_event *e_solicit;
51 static int test_client_basic(sd_event *e) {
52 sd_dhcp6_client *client;
55 printf("* %s\n", __FUNCTION__);
57 assert_se(sd_dhcp6_client_new(&client) >= 0);
60 assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
62 assert_se(sd_dhcp6_client_set_index(client, 15) == 0);
63 assert_se(sd_dhcp6_client_set_index(client, -42) == -EINVAL);
64 assert_se(sd_dhcp6_client_set_index(client, -1) == 0);
65 assert_se(sd_dhcp6_client_set_index(client, 42) >= 0);
67 assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
69 assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
71 assert_se(sd_dhcp6_client_detach_event(client) >= 0);
72 assert_se(!sd_dhcp6_client_unref(client));
77 static int test_option(sd_event *e) {
80 0x00, DHCP6_OPTION_ORO, 0x00, 0x07,
81 'A', 'B', 'C', 'D', 'E', 'F', 'G',
82 0x00, DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
83 '1', '2', '3', '4', '5', '6', '7', '8', '9',
88 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
95 uint8_t *optval, *buf, *out;
96 size_t zero = 0, pos = 3;
97 size_t buflen = sizeof(packet), outlen = sizeof(result);
100 printf("* %s\n", __FUNCTION__);
102 assert_se(buflen == outlen);
104 assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen,
105 &optval) == -ENOMSG);
112 assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
115 assert_se(buf == &packet[pos]);
116 assert_se(optcode == DHCP6_OPTION_ORO);
117 assert_se(optlen == 7);
118 assert_se(buflen + pos == sizeof(packet));
120 assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
122 assert_se(out == &result[pos]);
123 assert_se(*out == 0x00);
125 assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
128 assert_se(buf == &packet[pos]);
129 assert_se(optcode == DHCP6_OPTION_VENDOR_CLASS);
130 assert_se(optlen == 9);
131 assert_se(buflen + pos == sizeof(packet));
133 assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
135 assert_se(out == &result[pos]);
136 assert_se(*out == 'B');
138 assert_se(memcmp(packet, result, sizeof(packet)) == 0);
143 static uint8_t msg_advertise[198] = {
144 0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e,
145 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30,
146 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03,
147 0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00,
148 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05,
149 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
150 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c,
151 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
152 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00,
153 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28,
154 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65,
155 0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65,
156 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66,
157 0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e,
158 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68,
159 0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8,
160 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b,
162 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
163 0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20,
164 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00,
165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
166 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19,
167 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d,
168 0x53, 0x00, 0x07, 0x00, 0x01, 0x00
171 static int test_advertise_option(sd_event *e) {
172 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
173 DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
174 uint8_t *optval, *opt = &msg_advertise[sizeof(DHCP6Message)];
176 size_t optlen, len = sizeof(msg_advertise);
178 uint8_t preference = 255;
179 struct in6_addr addr;
180 uint32_t lt_pref, lt_valid;
182 bool opt_clientid = false;
185 printf("* %s\n", __FUNCTION__);
187 assert_se(dhcp6_lease_new(&lease) >= 0);
189 assert_se(advertise->type == DHCP6_ADVERTISE);
190 assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) ==
193 while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen,
197 case DHCP6_OPTION_CLIENTID:
198 assert_se(optlen == 14);
203 case DHCP6_OPTION_IA_NA:
204 assert_se(optlen == 94);
205 assert_se(!memcmp(optval, &msg_advertise[26], optlen));
207 val = htobe32(0x0ecfa37d);
208 assert_se(!memcmp(optval, &val, sizeof(val)));
211 assert_se(!memcmp(optval + 4, &val, sizeof(val)));
214 assert_se(!memcmp(optval + 8, &val, sizeof(val)));
216 assert_se(dhcp6_option_parse_ia(&optval, &optlen,
222 case DHCP6_OPTION_SERVERID:
223 assert_se(optlen == 14);
224 assert_se(!memcmp(optval, &msg_advertise[179], optlen));
226 assert_se(dhcp6_lease_set_serverid(lease, optval,
230 case DHCP6_OPTION_PREFERENCE:
231 assert_se(optlen == 1);
234 assert_se(dhcp6_lease_set_preference(lease,
244 assert_se(r == -ENOMSG);
246 assert_se(opt_clientid);
248 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
250 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
251 assert_se(lt_pref == 150);
252 assert_se(lt_valid == 180);
253 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
254 <_valid) == -ENOMSG);
256 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
258 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
259 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
260 <_valid) == -ENOMSG);
261 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
262 <_valid) == -ENOMSG);
263 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
265 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
266 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
267 <_valid) == -ENOMSG);
269 assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0);
270 assert_se(len == 14);
271 assert_se(!memcmp(opt, &msg_advertise[179], len));
273 assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
274 assert_se(preference == 0);
279 static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
280 assert_not_reached("Test case should have completed in 2 seconds");
285 int detect_vm(const char **id) {
289 int detect_container(const char **id) {
293 int detect_virtualization(const char **id) {
297 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
298 assert_se(index == test_index);
300 if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
303 return test_dhcp_fd[0];
306 static int verify_solicit(DHCP6Message *solicit, uint8_t *option, size_t len) {
310 bool found_clientid = false, found_iana = false;
313 assert_se(solicit->type == DHCP6_SOLICIT);
315 while ((r = dhcp6_option_parse(&option, &len,
316 &optcode, &optlen, &optval)) >= 0) {
318 case DHCP6_OPTION_CLIENTID:
319 assert_se(!found_clientid);
320 found_clientid = true;
322 assert_se(optlen == 14);
326 case DHCP6_OPTION_IA_NA:
327 assert_se(!found_iana);
330 assert_se(optlen == 12);
336 assert_se(r == -ENOMSG);
337 assert_se(found_clientid && found_iana);
339 sd_event_exit(e_solicit, 0);
344 int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
345 const void *packet, size_t len) {
346 struct in6_addr mcast =
347 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
348 DHCP6Message *message;
351 assert_se(s == test_dhcp_fd[0]);
352 assert_se(server_address);
354 assert_se(len > sizeof(DHCP6Message) + 4);
356 assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
358 message = (DHCP6Message *)packet;
359 option = (uint8_t *)(message + 1);
360 len -= sizeof(DHCP6Message);
362 assert_se(message->transaction_id & 0x00ffffff);
364 verify_solicit(message, option, len);
369 static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
371 sd_event *e = userdata;
376 printf(" got DHCPv6 event %d\n", event);
381 static int test_client_solicit(sd_event *e) {
382 sd_dhcp6_client *client;
383 usec_t time_now = now(CLOCK_MONOTONIC);
386 printf("* %s\n", __FUNCTION__);
388 assert_se(sd_dhcp6_client_new(&client) >= 0);
391 assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
393 assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
394 assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
396 assert_se(sd_dhcp6_client_set_callback(client,
397 test_client_solicit_cb, e) >= 0);
399 assert_se(sd_event_add_time(e, &hangcheck, CLOCK_MONOTONIC,
400 time_now + 2 * USEC_PER_SEC, 0,
401 test_hangcheck, NULL) >= 0);
405 assert_se(sd_dhcp6_client_start(client) >= 0);
409 hangcheck = sd_event_source_unref(hangcheck);
411 assert_se(!sd_dhcp6_client_unref(client));
413 test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
418 int main(int argc, char *argv[]) {
419 _cleanup_event_unref_ sd_event *e;
421 assert_se(sd_event_new(&e) >= 0);
423 log_set_max_level(LOG_DEBUG);
424 log_parse_environment();
427 test_client_basic(e);
429 test_advertise_option(e);
430 test_client_solicit(e);
432 assert_se(!sd_event_unref(e));