]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/test-dhcp6-client.c
sd-dhcp6-client: Receive and parse a reply and set T1 and T2 timers
[thirdparty/systemd.git] / src / libsystemd-network / test-dhcp6-client.c
CommitLineData
813e3a6f
PF
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright (C) 2014 Intel Corporation. All rights reserved.
7
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.
12
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.
17
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/>.
20***/
21
22#include <stdbool.h>
23#include <stdio.h>
2ea8857e
PF
24#include <sys/types.h>
25#include <sys/socket.h>
26#include <unistd.h>
27#include <net/ethernet.h>
813e3a6f 28
2ea8857e 29#include "socket-util.h"
813e3a6f
PF
30#include "macro.h"
31#include "sd-event.h"
32#include "event-util.h"
2ea8857e 33#include "virt.h"
813e3a6f
PF
34
35#include "sd-dhcp6-client.h"
36#include "dhcp6-protocol.h"
f12ed3bf 37#include "dhcp6-internal.h"
859cca44 38#include "dhcp6-lease-internal.h"
813e3a6f
PF
39
40static struct ether_addr mac_addr = {
41 .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
42};
43
44static bool verbose = false;
45
2ea8857e
PF
46static sd_event_source *hangcheck;
47static int test_dhcp_fd[2];
48static int test_index = 42;
5e256ea7
PF
49static int test_client_message_num;
50static be32_t test_iaid = 0;
51static uint8_t test_duid[14] = { };
2ea8857e
PF
52static sd_event *e_solicit;
53
813e3a6f
PF
54static int test_client_basic(sd_event *e) {
55 sd_dhcp6_client *client;
56
57 if (verbose)
58 printf("* %s\n", __FUNCTION__);
59
60 assert_se(sd_dhcp6_client_new(&client) >= 0);
61 assert_se(client);
62
63 assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
64
65 assert_se(sd_dhcp6_client_set_index(client, 15) == 0);
66 assert_se(sd_dhcp6_client_set_index(client, -42) == -EINVAL);
67 assert_se(sd_dhcp6_client_set_index(client, -1) == 0);
68 assert_se(sd_dhcp6_client_set_index(client, 42) >= 0);
69
70 assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
71
72 assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
73
74 assert_se(sd_dhcp6_client_detach_event(client) >= 0);
75 assert_se(!sd_dhcp6_client_unref(client));
76
77 return 0;
78}
79
f12ed3bf
PF
80static int test_option(sd_event *e) {
81 uint8_t packet[] = {
82 'F', 'O', 'O',
83 0x00, DHCP6_OPTION_ORO, 0x00, 0x07,
84 'A', 'B', 'C', 'D', 'E', 'F', 'G',
85 0x00, DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
86 '1', '2', '3', '4', '5', '6', '7', '8', '9',
87 'B', 'A', 'R',
88 };
89 uint8_t result[] = {
90 'F', 'O', 'O',
91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
93 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94 'B', 'A', 'R',
95 };
96 uint16_t optcode;
97 size_t optlen;
98 uint8_t *optval, *buf, *out;
99 size_t zero = 0, pos = 3;
100 size_t buflen = sizeof(packet), outlen = sizeof(result);
101
102 if (verbose)
103 printf("* %s\n", __FUNCTION__);
104
105 assert_se(buflen == outlen);
106
107 assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen,
108 &optval) == -ENOMSG);
109
110 buflen -= 3;
111 buf = &packet[3];
112 outlen -= 3;
113 out = &result[3];
114
115 assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
116 &optval) >= 0);
117 pos += 4 + optlen;
118 assert_se(buf == &packet[pos]);
119 assert_se(optcode == DHCP6_OPTION_ORO);
120 assert_se(optlen == 7);
121 assert_se(buflen + pos == sizeof(packet));
122
123 assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
124 optval) >= 0);
125 assert_se(out == &result[pos]);
126 assert_se(*out == 0x00);
127
128 assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
129 &optval) >= 0);
130 pos += 4 + optlen;
131 assert_se(buf == &packet[pos]);
132 assert_se(optcode == DHCP6_OPTION_VENDOR_CLASS);
133 assert_se(optlen == 9);
134 assert_se(buflen + pos == sizeof(packet));
135
136 assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
137 optval) >= 0);
138 assert_se(out == &result[pos]);
139 assert_se(*out == 'B');
140
141 assert_se(memcmp(packet, result, sizeof(packet)) == 0);
142
143 return 0;
144}
145
859cca44
PF
146static uint8_t msg_advertise[198] = {
147 0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e,
148 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30,
149 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03,
150 0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00,
151 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05,
152 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
153 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c,
154 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
155 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00,
156 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28,
157 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65,
158 0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65,
159 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66,
160 0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e,
161 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68,
162 0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8,
163 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00,
164 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b,
165 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
166 0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20,
167 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
169 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19,
170 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d,
171 0x53, 0x00, 0x07, 0x00, 0x01, 0x00
172};
173
174static int test_advertise_option(sd_event *e) {
175 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
176 DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
177 uint8_t *optval, *opt = &msg_advertise[sizeof(DHCP6Message)];
178 uint16_t optcode;
179 size_t optlen, len = sizeof(msg_advertise);
180 be32_t val;
181 uint8_t preference = 255;
182 struct in6_addr addr;
183 uint32_t lt_pref, lt_valid;
184 int r;
185 bool opt_clientid = false;
186
187 if (verbose)
188 printf("* %s\n", __FUNCTION__);
189
190 assert_se(dhcp6_lease_new(&lease) >= 0);
191
192 assert_se(advertise->type == DHCP6_ADVERTISE);
193 assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) ==
194 0x0fb4e5);
195
196 while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen,
197 &optval)) >= 0) {
198
199 switch(optcode) {
200 case DHCP6_OPTION_CLIENTID:
201 assert_se(optlen == 14);
202
203 opt_clientid = true;
204 break;
205
206 case DHCP6_OPTION_IA_NA:
207 assert_se(optlen == 94);
208 assert_se(!memcmp(optval, &msg_advertise[26], optlen));
209
210 val = htobe32(0x0ecfa37d);
211 assert_se(!memcmp(optval, &val, sizeof(val)));
212
213 val = htobe32(80);
214 assert_se(!memcmp(optval + 4, &val, sizeof(val)));
215
216 val = htobe32(120);
217 assert_se(!memcmp(optval + 8, &val, sizeof(val)));
218
219 assert_se(dhcp6_option_parse_ia(&optval, &optlen,
220 optcode,
221 &lease->ia) >= 0);
222
223 break;
224
225 case DHCP6_OPTION_SERVERID:
226 assert_se(optlen == 14);
227 assert_se(!memcmp(optval, &msg_advertise[179], optlen));
228
229 assert_se(dhcp6_lease_set_serverid(lease, optval,
230 optlen) >= 0);
231 break;
232
233 case DHCP6_OPTION_PREFERENCE:
234 assert_se(optlen == 1);
235 assert_se(!*optval);
236
237 assert_se(dhcp6_lease_set_preference(lease,
238 *optval) >= 0);
239 break;
240
241 default:
242 break;
243 }
244 }
245
246
247 assert_se(r == -ENOMSG);
248
249 assert_se(opt_clientid);
250
251 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
252 &lt_valid) >= 0);
253 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
254 assert_se(lt_pref == 150);
255 assert_se(lt_valid == 180);
256 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
257 &lt_valid) == -ENOMSG);
258
259 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
260 &lt_valid) >= 0);
261 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
262 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
263 &lt_valid) == -ENOMSG);
264 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
265 &lt_valid) == -ENOMSG);
266 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
267 &lt_valid) >= 0);
268 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
269 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
270 &lt_valid) == -ENOMSG);
271
272 assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0);
273 assert_se(len == 14);
274 assert_se(!memcmp(opt, &msg_advertise[179], len));
275
276 assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
277 assert_se(preference == 0);
278
279 return 0;
280}
281
2ea8857e
PF
282static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
283 assert_not_reached("Test case should have completed in 2 seconds");
284
285 return 0;
286}
287
288int detect_vm(const char **id) {
289 return 1;
290}
291
292int detect_container(const char **id) {
293 return 1;
294}
295
296int detect_virtualization(const char **id) {
297 return 1;
298}
299
300int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
301 assert_se(index == test_index);
302
303 if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
304 return -errno;
305
306 return test_dhcp_fd[0];
307}
308
5e256ea7
PF
309static int test_client_send_reply(DHCP6Message *request) {
310 return 0;
311}
312
313static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
314 size_t len) {
315 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
316 uint8_t *optval;
317 uint16_t optcode;
318 size_t optlen;
319 bool found_clientid = false, found_iana = false, found_serverid = false;
320 int r;
321 struct in6_addr addr;
322 be32_t val;
323 uint32_t lt_pref, lt_valid;
324
325 assert_se(request->type == DHCP6_REQUEST);
326
327 assert_se(dhcp6_lease_new(&lease) >= 0);
328
329 while ((r = dhcp6_option_parse(&option, &len,
330 &optcode, &optlen, &optval)) >= 0) {
331 switch(optcode) {
332 case DHCP6_OPTION_CLIENTID:
333 assert_se(!found_clientid);
334 found_clientid = true;
335
336 assert_se(!memcmp(optval, &test_duid,
337 sizeof(test_duid)));
338
339 break;
340
341 case DHCP6_OPTION_IA_NA:
342 assert_se(!found_iana);
343 found_iana = true;
344
345
346 assert_se(optlen == 40);
347 assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid)));
348
349 val = htobe32(80);
350 assert_se(!memcmp(optval + 4, &val, sizeof(val)));
351
352 val = htobe32(120);
353 assert_se(!memcmp(optval + 8, &val, sizeof(val)));
354
355 assert_se(!dhcp6_option_parse_ia(&optval, &optlen,
356 optcode, &lease->ia));
357
358 break;
359
360 case DHCP6_OPTION_SERVERID:
361 assert_se(!found_serverid);
362 found_serverid = true;
363
364 assert_se(optlen == 14);
365 assert_se(!memcmp(&msg_advertise[179], optval, optlen));
366
367 break;
368 }
369 }
370
371 assert_se(r == -ENOMSG);
372 assert_se(found_clientid && found_iana && found_serverid);
373
374 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
375 &lt_valid) >= 0);
376 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
377 assert_se(lt_pref == 150);
378 assert_se(lt_valid == 180);
379
380 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
381 &lt_valid) == -ENOMSG);
382
383 sd_event_exit(e_solicit, 0);
384
385 return 0;
386}
387
388static int test_client_send_advertise(DHCP6Message *solicit)
389{
390 DHCP6Message advertise;
391
392 advertise.transaction_id = solicit->transaction_id;
393 advertise.type = DHCP6_ADVERTISE;
394
395 memcpy(msg_advertise, &advertise.transaction_id, 4);
396
397 memcpy(&msg_advertise[8], test_duid, sizeof(test_duid));
398
399 memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid));
400
401 assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise))
402 == sizeof(msg_advertise));
403
404 return 0;
405}
406
407static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
408 size_t len) {
2ea8857e
PF
409 uint8_t *optval;
410 uint16_t optcode;
411 size_t optlen;
412 bool found_clientid = false, found_iana = false;
413 int r;
414
415 assert_se(solicit->type == DHCP6_SOLICIT);
416
417 while ((r = dhcp6_option_parse(&option, &len,
418 &optcode, &optlen, &optval)) >= 0) {
419 switch(optcode) {
420 case DHCP6_OPTION_CLIENTID:
421 assert_se(!found_clientid);
422 found_clientid = true;
423
5e256ea7
PF
424 assert_se(optlen == sizeof(test_duid));
425 memcpy(&test_duid, optval, sizeof(test_duid));
2ea8857e
PF
426
427 break;
428
429 case DHCP6_OPTION_IA_NA:
430 assert_se(!found_iana);
431 found_iana = true;
432
433 assert_se(optlen == 12);
434
5e256ea7
PF
435 memcpy(&test_iaid, optval, sizeof(test_iaid));
436
2ea8857e
PF
437 break;
438 }
439 }
440
441 assert_se(r == -ENOMSG);
442 assert_se(found_clientid && found_iana);
443
2ea8857e
PF
444 return 0;
445}
446
447int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
448 const void *packet, size_t len) {
449 struct in6_addr mcast =
450 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
451 DHCP6Message *message;
452 uint8_t *option;
453
454 assert_se(s == test_dhcp_fd[0]);
455 assert_se(server_address);
456 assert_se(packet);
457 assert_se(len > sizeof(DHCP6Message) + 4);
458
459 assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
460
461 message = (DHCP6Message *)packet;
462 option = (uint8_t *)(message + 1);
463 len -= sizeof(DHCP6Message);
464
465 assert_se(message->transaction_id & 0x00ffffff);
466
5e256ea7
PF
467 if (test_client_message_num == 0) {
468 test_client_verify_solicit(message, option, len);
469 test_client_send_advertise(message);
470 test_client_message_num++;
471 } else if (test_client_message_num == 1) {
472 test_client_verify_request(message, option, len);
473 test_client_send_reply(message);
474 test_client_message_num++;
475 }
2ea8857e
PF
476
477 return len;
478}
479
480static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
481 void *userdata) {
482 sd_event *e = userdata;
483
484 assert_se(e);
485
486 if (verbose)
487 printf(" got DHCPv6 event %d\n", event);
488
489 sd_event_exit(e, 0);
490}
491
492static int test_client_solicit(sd_event *e) {
493 sd_dhcp6_client *client;
494 usec_t time_now = now(CLOCK_MONOTONIC);
495
496 if (verbose)
497 printf("* %s\n", __FUNCTION__);
498
499 assert_se(sd_dhcp6_client_new(&client) >= 0);
500 assert_se(client);
501
502 assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
503
504 assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
505 assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
506
507 assert_se(sd_dhcp6_client_set_callback(client,
508 test_client_solicit_cb, e) >= 0);
509
510 assert_se(sd_event_add_time(e, &hangcheck, CLOCK_MONOTONIC,
511 time_now + 2 * USEC_PER_SEC, 0,
512 test_hangcheck, NULL) >= 0);
513
514 e_solicit = e;
515
516 assert_se(sd_dhcp6_client_start(client) >= 0);
517
518 sd_event_loop(e);
519
520 hangcheck = sd_event_source_unref(hangcheck);
521
522 assert_se(!sd_dhcp6_client_unref(client));
523
524 test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
525
526 return 0;
527}
528
813e3a6f
PF
529int main(int argc, char *argv[]) {
530 _cleanup_event_unref_ sd_event *e;
531
532 assert_se(sd_event_new(&e) >= 0);
533
534 log_set_max_level(LOG_DEBUG);
535 log_parse_environment();
536 log_open();
537
538 test_client_basic(e);
f12ed3bf 539 test_option(e);
859cca44 540 test_advertise_option(e);
2ea8857e 541 test_client_solicit(e);
f12ed3bf
PF
542
543 assert_se(!sd_event_unref(e));
813e3a6f
PF
544
545 return 0;
546}