chiark / gitweb /
sd-dhcp6-client: Add Advertise sending for test case
[elogind.git] / src / libsystemd-network / test-dhcp6-client.c
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>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <unistd.h>
27 #include <net/ethernet.h>
28
29 #include "socket-util.h"
30 #include "macro.h"
31 #include "sd-event.h"
32 #include "event-util.h"
33 #include "virt.h"
34
35 #include "sd-dhcp6-client.h"
36 #include "dhcp6-protocol.h"
37 #include "dhcp6-internal.h"
38 #include "dhcp6-lease-internal.h"
39
40 static struct ether_addr mac_addr = {
41         .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
42 };
43
44 static bool verbose = false;
45
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] = { };
52 static sd_event *e_solicit;
53
54 static 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
80 static 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
146 static 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
174 static 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
282 static 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
288 int detect_vm(const char **id) {
289         return 1;
290 }
291
292 int detect_container(const char **id) {
293         return 1;
294 }
295
296 int detect_virtualization(const char **id) {
297         return 1;
298 }
299
300 int 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
309 static int test_client_send_reply(DHCP6Message *request) {
310         return 0;
311 }
312
313 static 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
388 static 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
407 static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
408                                       size_t len) {
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
424                         assert_se(optlen == sizeof(test_duid));
425                         memcpy(&test_duid, optval, sizeof(test_duid));
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
435                         memcpy(&test_iaid, optval, sizeof(test_iaid));
436
437                         break;
438                 }
439         }
440
441         assert_se(r == -ENOMSG);
442         assert_se(found_clientid && found_iana);
443
444         return 0;
445 }
446
447 int 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
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         }
476
477         return len;
478 }
479
480 static 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
492 static 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
529 int 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);
539         test_option(e);
540         test_advertise_option(e);
541         test_client_solicit(e);
542
543         assert_se(!sd_event_unref(e));
544
545         return 0;
546 }