chiark / gitweb /
sd-dhcp-client: return NULL from _unref() like the other sd-* libraries
[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
53 static int test_client_basic(sd_event *e) {
54         sd_dhcp6_client *client;
55
56         if (verbose)
57                 printf("* %s\n", __FUNCTION__);
58
59         assert_se(sd_dhcp6_client_new(&client) >= 0);
60         assert_se(client);
61
62         assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
63
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);
68
69         assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
70
71         assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
72
73         assert_se(sd_dhcp6_client_detach_event(client) >= 0);
74         assert_se(!sd_dhcp6_client_unref(client));
75
76         return 0;
77 }
78
79 static int test_option(sd_event *e) {
80         uint8_t packet[] = {
81                 'F', 'O', 'O',
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',
86                 'B', 'A', 'R',
87         };
88         uint8_t result[] = {
89                 'F', 'O', 'O',
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,
93                 'B', 'A', 'R',
94         };
95         uint16_t optcode;
96         size_t optlen;
97         uint8_t *optval, *buf, *out;
98         size_t zero = 0, pos = 3;
99         size_t buflen = sizeof(packet), outlen = sizeof(result);
100
101         if (verbose)
102                 printf("* %s\n", __FUNCTION__);
103
104         assert_se(buflen == outlen);
105
106         assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen,
107                                      &optval) == -ENOMSG);
108
109         buflen -= 3;
110         buf = &packet[3];
111         outlen -= 3;
112         out = &result[3];
113
114         assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
115                                      &optval) >= 0);
116         pos += 4 + 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));
121
122         assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
123                                       optval) >= 0);
124         assert_se(out == &result[pos]);
125         assert_se(*out == 0x00);
126
127         assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
128                                      &optval) >= 0);
129         pos += 4 + 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));
134
135         assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
136                                       optval) >= 0);
137         assert_se(out == &result[pos]);
138         assert_se(*out == 'B');
139
140         assert_se(memcmp(packet, result, sizeof(packet)) == 0);
141
142         return 0;
143 }
144
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
171 };
172
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
196 };
197
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)];
202         uint16_t optcode;
203         size_t optlen, len = sizeof(msg_advertise);
204         be32_t val;
205         uint8_t preference = 255;
206         struct in6_addr addr;
207         uint32_t lt_pref, lt_valid;
208         int r;
209         bool opt_clientid = false;
210
211         if (verbose)
212                 printf("* %s\n", __FUNCTION__);
213
214         assert_se(dhcp6_lease_new(&lease) >= 0);
215
216         assert_se(advertise->type == DHCP6_ADVERTISE);
217         assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) ==
218                   0x0fb4e5);
219
220         while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen,
221                                        &optval)) >= 0) {
222
223                 switch(optcode) {
224                 case DHCP6_OPTION_CLIENTID:
225                         assert_se(optlen == 14);
226
227                         opt_clientid = true;
228                         break;
229
230                 case DHCP6_OPTION_IA_NA:
231                         assert_se(optlen == 94);
232                         assert_se(!memcmp(optval, &msg_advertise[26], optlen));
233
234                         val = htobe32(0x0ecfa37d);
235                         assert_se(!memcmp(optval, &val, sizeof(val)));
236
237                         val = htobe32(80);
238                         assert_se(!memcmp(optval + 4, &val, sizeof(val)));
239
240                         val = htobe32(120);
241                         assert_se(!memcmp(optval + 8, &val, sizeof(val)));
242
243                         assert_se(dhcp6_option_parse_ia(&optval, &optlen,
244                                                         optcode,
245                                                         &lease->ia) >= 0);
246
247                         break;
248
249                 case DHCP6_OPTION_SERVERID:
250                         assert_se(optlen == 14);
251                         assert_se(!memcmp(optval, &msg_advertise[179], optlen));
252
253                         assert_se(dhcp6_lease_set_serverid(lease, optval,
254                                                            optlen) >= 0);
255                         break;
256
257                 case DHCP6_OPTION_PREFERENCE:
258                         assert_se(optlen == 1);
259                         assert_se(!*optval);
260
261                         assert_se(dhcp6_lease_set_preference(lease,
262                                                              *optval) >= 0);
263                         break;
264
265                 default:
266                         break;
267                 }
268         }
269
270
271         assert_se(r == -ENOMSG);
272
273         assert_se(opt_clientid);
274
275         assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
276                                                    &lt_valid) >= 0);
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, &lt_pref,
281                                                   &lt_valid) == -ENOMSG);
282
283         assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
284                                                    &lt_valid) >= 0);
285         assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
286         assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
287                                                   &lt_valid) == -ENOMSG);
288         assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
289                                                   &lt_valid) == -ENOMSG);
290         assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
291                                                    &lt_valid) >= 0);
292         assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
293         assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
294                                                   &lt_valid) == -ENOMSG);
295
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));
299
300         assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
301         assert_se(preference == 0);
302
303         return 0;
304 }
305
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");
308
309         return 0;
310 }
311
312 int detect_vm(const char **id) {
313         return 1;
314 }
315
316 int detect_container(const char **id) {
317         return 1;
318 }
319
320 int detect_virtualization(const char **id) {
321         return 1;
322 }
323
324 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
325         assert_se(index == test_index);
326
327         if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
328                 return -errno;
329
330         return test_dhcp_fd[0];
331 }
332
333 static int test_client_send_reply(DHCP6Message *request) {
334         DHCP6Message reply;
335
336         reply.transaction_id = request->transaction_id;
337         reply.type = DHCP6_REPLY;
338
339         memcpy(msg_reply, &reply.transaction_id, 4);
340
341         memcpy(&msg_reply[26], test_duid, sizeof(test_duid));
342
343         memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid));
344
345         assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply))
346                   == sizeof(msg_reply));
347
348         return 0;
349 }
350
351 static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
352                                       size_t len) {
353         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
354         uint8_t *optval;
355         uint16_t optcode;
356         size_t optlen;
357         bool found_clientid = false, found_iana = false, found_serverid = false;
358         int r;
359         struct in6_addr addr;
360         be32_t val;
361         uint32_t lt_pref, lt_valid;
362
363         assert_se(request->type == DHCP6_REQUEST);
364
365         assert_se(dhcp6_lease_new(&lease) >= 0);
366
367         while ((r = dhcp6_option_parse(&option, &len,
368                                        &optcode, &optlen, &optval)) >= 0) {
369                 switch(optcode) {
370                 case DHCP6_OPTION_CLIENTID:
371                         assert_se(!found_clientid);
372                         found_clientid = true;
373
374                         assert_se(!memcmp(optval, &test_duid,
375                                           sizeof(test_duid)));
376
377                         break;
378
379                 case DHCP6_OPTION_IA_NA:
380                         assert_se(!found_iana);
381                         found_iana = true;
382
383
384                         assert_se(optlen == 40);
385                         assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid)));
386
387                         val = htobe32(80);
388                         assert_se(!memcmp(optval + 4, &val, sizeof(val)));
389
390                         val = htobe32(120);
391                         assert_se(!memcmp(optval + 8, &val, sizeof(val)));
392
393                         assert_se(!dhcp6_option_parse_ia(&optval, &optlen,
394                                                          optcode, &lease->ia));
395
396                         break;
397
398                 case DHCP6_OPTION_SERVERID:
399                         assert_se(!found_serverid);
400                         found_serverid = true;
401
402                         assert_se(optlen == 14);
403                         assert_se(!memcmp(&msg_advertise[179], optval, optlen));
404
405                         break;
406                 }
407         }
408
409         assert_se(r == -ENOMSG);
410         assert_se(found_clientid && found_iana && found_serverid);
411
412         assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
413                                                    &lt_valid) >= 0);
414         assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
415         assert_se(lt_pref == 150);
416         assert_se(lt_valid == 180);
417
418         assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
419                                                   &lt_valid) == -ENOMSG);
420
421         return 0;
422 }
423
424 static int test_client_send_advertise(DHCP6Message *solicit)
425 {
426         DHCP6Message advertise;
427
428         advertise.transaction_id = solicit->transaction_id;
429         advertise.type = DHCP6_ADVERTISE;
430
431         memcpy(msg_advertise, &advertise.transaction_id, 4);
432
433         memcpy(&msg_advertise[8], test_duid, sizeof(test_duid));
434
435         memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid));
436
437         assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise))
438                   == sizeof(msg_advertise));
439
440         return 0;
441 }
442
443 static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
444                                       size_t len) {
445         uint8_t *optval;
446         uint16_t optcode;
447         size_t optlen;
448         bool found_clientid = false, found_iana = false;
449         int r;
450
451         assert_se(solicit->type == DHCP6_SOLICIT);
452
453         while ((r = dhcp6_option_parse(&option, &len,
454                                        &optcode, &optlen, &optval)) >= 0) {
455                 switch(optcode) {
456                 case DHCP6_OPTION_CLIENTID:
457                         assert_se(!found_clientid);
458                         found_clientid = true;
459
460                         assert_se(optlen == sizeof(test_duid));
461                         memcpy(&test_duid, optval, sizeof(test_duid));
462
463                         break;
464
465                 case DHCP6_OPTION_IA_NA:
466                         assert_se(!found_iana);
467                         found_iana = true;
468
469                         assert_se(optlen == 12);
470
471                         memcpy(&test_iaid, optval, sizeof(test_iaid));
472
473                         break;
474                 }
475         }
476
477         assert_se(r == -ENOMSG);
478         assert_se(found_clientid && found_iana);
479
480         return 0;
481 }
482
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;
488         uint8_t *option;
489
490         assert_se(s == test_dhcp_fd[0]);
491         assert_se(server_address);
492         assert_se(packet);
493         assert_se(len > sizeof(DHCP6Message) + 4);
494
495         assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
496
497         message = (DHCP6Message *)packet;
498         option = (uint8_t *)(message + 1);
499         len -= sizeof(DHCP6Message);
500
501         assert_se(message->transaction_id & 0x00ffffff);
502
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++;
511         }
512
513         return len;
514 }
515
516 static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
517                                    void *userdata) {
518         sd_event *e = userdata;
519
520         assert_se(e);
521         assert_se(event == DHCP6_EVENT_IP_ACQUIRE);
522
523         if (verbose)
524                 printf("  got DHCPv6 event %d\n", event);
525
526         sd_event_exit(e, 0);
527 }
528
529 static int test_client_solicit(sd_event *e) {
530         sd_dhcp6_client *client;
531         usec_t time_now = now(CLOCK_MONOTONIC);
532
533         if (verbose)
534                 printf("* %s\n", __FUNCTION__);
535
536         assert_se(sd_dhcp6_client_new(&client) >= 0);
537         assert_se(client);
538
539         assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
540
541         assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
542         assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
543
544         assert_se(sd_dhcp6_client_set_callback(client,
545                                                test_client_solicit_cb, e) >= 0);
546
547         assert_se(sd_event_add_time(e, &hangcheck, CLOCK_MONOTONIC,
548                                     time_now + 2 * USEC_PER_SEC, 0,
549                                     test_hangcheck, NULL) >= 0);
550
551         assert_se(sd_dhcp6_client_start(client) >= 0);
552
553         sd_event_loop(e);
554
555         hangcheck = sd_event_source_unref(hangcheck);
556
557         assert_se(!sd_dhcp6_client_unref(client));
558
559         test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
560
561         return 0;
562 }
563
564 int main(int argc, char *argv[]) {
565         _cleanup_event_unref_ sd_event *e;
566
567         assert_se(sd_event_new(&e) >= 0);
568
569         log_set_max_level(LOG_DEBUG);
570         log_parse_environment();
571         log_open();
572
573         test_client_basic(e);
574         test_option(e);
575         test_advertise_option(e);
576         test_client_solicit(e);
577
578         assert_se(!sd_event_unref(e));
579
580         return 0;
581 }