chiark / gitweb /
sd-dhcp6-client: Add test case for Advertise message parsing
[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 sd_event *e_solicit;
50
51 static int test_client_basic(sd_event *e) {
52         sd_dhcp6_client *client;
53
54         if (verbose)
55                 printf("* %s\n", __FUNCTION__);
56
57         assert_se(sd_dhcp6_client_new(&client) >= 0);
58         assert_se(client);
59
60         assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
61
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);
66
67         assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
68
69         assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
70
71         assert_se(sd_dhcp6_client_detach_event(client) >= 0);
72         assert_se(!sd_dhcp6_client_unref(client));
73
74         return 0;
75 }
76
77 static int test_option(sd_event *e) {
78         uint8_t packet[] = {
79                 'F', 'O', 'O',
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',
84                 'B', 'A', 'R',
85         };
86         uint8_t result[] = {
87                 'F', 'O', 'O',
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,
91                 'B', 'A', 'R',
92         };
93         uint16_t optcode;
94         size_t optlen;
95         uint8_t *optval, *buf, *out;
96         size_t zero = 0, pos = 3;
97         size_t buflen = sizeof(packet), outlen = sizeof(result);
98
99         if (verbose)
100                 printf("* %s\n", __FUNCTION__);
101
102         assert_se(buflen == outlen);
103
104         assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen,
105                                      &optval) == -ENOMSG);
106
107         buflen -= 3;
108         buf = &packet[3];
109         outlen -= 3;
110         out = &result[3];
111
112         assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
113                                      &optval) >= 0);
114         pos += 4 + 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));
119
120         assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
121                                       optval) >= 0);
122         assert_se(out == &result[pos]);
123         assert_se(*out == 0x00);
124
125         assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
126                                      &optval) >= 0);
127         pos += 4 + 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));
132
133         assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
134                                       optval) >= 0);
135         assert_se(out == &result[pos]);
136         assert_se(*out == 'B');
137
138         assert_se(memcmp(packet, result, sizeof(packet)) == 0);
139
140         return 0;
141 }
142
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
169 };
170
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)];
175         uint16_t optcode;
176         size_t optlen, len = sizeof(msg_advertise);
177         be32_t val;
178         uint8_t preference = 255;
179         struct in6_addr addr;
180         uint32_t lt_pref, lt_valid;
181         int r;
182         bool opt_clientid = false;
183
184         if (verbose)
185                 printf("* %s\n", __FUNCTION__);
186
187         assert_se(dhcp6_lease_new(&lease) >= 0);
188
189         assert_se(advertise->type == DHCP6_ADVERTISE);
190         assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) ==
191                   0x0fb4e5);
192
193         while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen,
194                                        &optval)) >= 0) {
195
196                 switch(optcode) {
197                 case DHCP6_OPTION_CLIENTID:
198                         assert_se(optlen == 14);
199
200                         opt_clientid = true;
201                         break;
202
203                 case DHCP6_OPTION_IA_NA:
204                         assert_se(optlen == 94);
205                         assert_se(!memcmp(optval, &msg_advertise[26], optlen));
206
207                         val = htobe32(0x0ecfa37d);
208                         assert_se(!memcmp(optval, &val, sizeof(val)));
209
210                         val = htobe32(80);
211                         assert_se(!memcmp(optval + 4, &val, sizeof(val)));
212
213                         val = htobe32(120);
214                         assert_se(!memcmp(optval + 8, &val, sizeof(val)));
215
216                         assert_se(dhcp6_option_parse_ia(&optval, &optlen,
217                                                         optcode,
218                                                         &lease->ia) >= 0);
219
220                         break;
221
222                 case DHCP6_OPTION_SERVERID:
223                         assert_se(optlen == 14);
224                         assert_se(!memcmp(optval, &msg_advertise[179], optlen));
225
226                         assert_se(dhcp6_lease_set_serverid(lease, optval,
227                                                            optlen) >= 0);
228                         break;
229
230                 case DHCP6_OPTION_PREFERENCE:
231                         assert_se(optlen == 1);
232                         assert_se(!*optval);
233
234                         assert_se(dhcp6_lease_set_preference(lease,
235                                                              *optval) >= 0);
236                         break;
237
238                 default:
239                         break;
240                 }
241         }
242
243
244         assert_se(r == -ENOMSG);
245
246         assert_se(opt_clientid);
247
248         assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
249                                                    &lt_valid) >= 0);
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, &lt_pref,
254                                                   &lt_valid) == -ENOMSG);
255
256         assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
257                                                    &lt_valid) >= 0);
258         assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
259         assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
260                                                   &lt_valid) == -ENOMSG);
261         assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
262                                                   &lt_valid) == -ENOMSG);
263         assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
264                                                    &lt_valid) >= 0);
265         assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
266         assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
267                                                   &lt_valid) == -ENOMSG);
268
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));
272
273         assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
274         assert_se(preference == 0);
275
276         return 0;
277 }
278
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");
281
282         return 0;
283 }
284
285 int detect_vm(const char **id) {
286         return 1;
287 }
288
289 int detect_container(const char **id) {
290         return 1;
291 }
292
293 int detect_virtualization(const char **id) {
294         return 1;
295 }
296
297 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
298         assert_se(index == test_index);
299
300         if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
301                 return -errno;
302
303         return test_dhcp_fd[0];
304 }
305
306 static int verify_solicit(DHCP6Message *solicit, uint8_t *option, size_t len) {
307         uint8_t *optval;
308         uint16_t optcode;
309         size_t optlen;
310         bool found_clientid = false, found_iana = false;
311         int r;
312
313         assert_se(solicit->type == DHCP6_SOLICIT);
314
315         while ((r = dhcp6_option_parse(&option, &len,
316                                        &optcode, &optlen, &optval)) >= 0) {
317                 switch(optcode) {
318                 case DHCP6_OPTION_CLIENTID:
319                         assert_se(!found_clientid);
320                         found_clientid = true;
321
322                         assert_se(optlen == 14);
323
324                         break;
325
326                 case DHCP6_OPTION_IA_NA:
327                         assert_se(!found_iana);
328                         found_iana = true;
329
330                         assert_se(optlen == 12);
331
332                         break;
333                 }
334         }
335
336         assert_se(r == -ENOMSG);
337         assert_se(found_clientid && found_iana);
338
339         sd_event_exit(e_solicit, 0);
340
341         return 0;
342 }
343
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;
349         uint8_t *option;
350
351         assert_se(s == test_dhcp_fd[0]);
352         assert_se(server_address);
353         assert_se(packet);
354         assert_se(len > sizeof(DHCP6Message) + 4);
355
356         assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
357
358         message = (DHCP6Message *)packet;
359         option = (uint8_t *)(message + 1);
360         len -= sizeof(DHCP6Message);
361
362         assert_se(message->transaction_id & 0x00ffffff);
363
364         verify_solicit(message, option, len);
365
366         return len;
367 }
368
369 static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
370                                    void *userdata) {
371         sd_event *e = userdata;
372
373         assert_se(e);
374
375         if (verbose)
376                 printf("  got DHCPv6 event %d\n", event);
377
378         sd_event_exit(e, 0);
379 }
380
381 static int test_client_solicit(sd_event *e) {
382         sd_dhcp6_client *client;
383         usec_t time_now = now(CLOCK_MONOTONIC);
384
385         if (verbose)
386                 printf("* %s\n", __FUNCTION__);
387
388         assert_se(sd_dhcp6_client_new(&client) >= 0);
389         assert_se(client);
390
391         assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
392
393         assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
394         assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
395
396         assert_se(sd_dhcp6_client_set_callback(client,
397                                                test_client_solicit_cb, e) >= 0);
398
399         assert_se(sd_event_add_time(e, &hangcheck, CLOCK_MONOTONIC,
400                                     time_now + 2 * USEC_PER_SEC, 0,
401                                     test_hangcheck, NULL) >= 0);
402
403         e_solicit = e;
404
405         assert_se(sd_dhcp6_client_start(client) >= 0);
406
407         sd_event_loop(e);
408
409         hangcheck = sd_event_source_unref(hangcheck);
410
411         assert_se(!sd_dhcp6_client_unref(client));
412
413         test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
414
415         return 0;
416 }
417
418 int main(int argc, char *argv[]) {
419         _cleanup_event_unref_ sd_event *e;
420
421         assert_se(sd_event_new(&e) >= 0);
422
423         log_set_max_level(LOG_DEBUG);
424         log_parse_environment();
425         log_open();
426
427         test_client_basic(e);
428         test_option(e);
429         test_advertise_option(e);
430         test_client_solicit(e);
431
432         assert_se(!sd_event_unref(e));
433
434         return 0;
435 }