chiark / gitweb /
sd-dhcp6-client: Add DHCPv6 Solicit 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
39 static struct ether_addr mac_addr = {
40         .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
41 };
42
43 static bool verbose = false;
44
45 static sd_event_source *hangcheck;
46 static int test_dhcp_fd[2];
47 static int test_index = 42;
48 static sd_event *e_solicit;
49
50 static int test_client_basic(sd_event *e) {
51         sd_dhcp6_client *client;
52
53         if (verbose)
54                 printf("* %s\n", __FUNCTION__);
55
56         assert_se(sd_dhcp6_client_new(&client) >= 0);
57         assert_se(client);
58
59         assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
60
61         assert_se(sd_dhcp6_client_set_index(client, 15) == 0);
62         assert_se(sd_dhcp6_client_set_index(client, -42) == -EINVAL);
63         assert_se(sd_dhcp6_client_set_index(client, -1) == 0);
64         assert_se(sd_dhcp6_client_set_index(client, 42) >= 0);
65
66         assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
67
68         assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
69
70         assert_se(sd_dhcp6_client_detach_event(client) >= 0);
71         assert_se(!sd_dhcp6_client_unref(client));
72
73         return 0;
74 }
75
76 static int test_option(sd_event *e) {
77         uint8_t packet[] = {
78                 'F', 'O', 'O',
79                 0x00, DHCP6_OPTION_ORO, 0x00, 0x07,
80                 'A', 'B', 'C', 'D', 'E', 'F', 'G',
81                 0x00, DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
82                 '1', '2', '3', '4', '5', '6', '7', '8', '9',
83                 'B', 'A', 'R',
84         };
85         uint8_t result[] = {
86                 'F', 'O', 'O',
87                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
90                 'B', 'A', 'R',
91         };
92         uint16_t optcode;
93         size_t optlen;
94         uint8_t *optval, *buf, *out;
95         size_t zero = 0, pos = 3;
96         size_t buflen = sizeof(packet), outlen = sizeof(result);
97
98         if (verbose)
99                 printf("* %s\n", __FUNCTION__);
100
101         assert_se(buflen == outlen);
102
103         assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen,
104                                      &optval) == -ENOMSG);
105
106         buflen -= 3;
107         buf = &packet[3];
108         outlen -= 3;
109         out = &result[3];
110
111         assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
112                                      &optval) >= 0);
113         pos += 4 + optlen;
114         assert_se(buf == &packet[pos]);
115         assert_se(optcode == DHCP6_OPTION_ORO);
116         assert_se(optlen == 7);
117         assert_se(buflen + pos == sizeof(packet));
118
119         assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
120                                       optval) >= 0);
121         assert_se(out == &result[pos]);
122         assert_se(*out == 0x00);
123
124         assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
125                                      &optval) >= 0);
126         pos += 4 + optlen;
127         assert_se(buf == &packet[pos]);
128         assert_se(optcode == DHCP6_OPTION_VENDOR_CLASS);
129         assert_se(optlen == 9);
130         assert_se(buflen + pos == sizeof(packet));
131
132         assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
133                                       optval) >= 0);
134         assert_se(out == &result[pos]);
135         assert_se(*out == 'B');
136
137         assert_se(memcmp(packet, result, sizeof(packet)) == 0);
138
139         return 0;
140 }
141
142 static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
143         assert_not_reached("Test case should have completed in 2 seconds");
144
145         return 0;
146 }
147
148 int detect_vm(const char **id) {
149         return 1;
150 }
151
152 int detect_container(const char **id) {
153         return 1;
154 }
155
156 int detect_virtualization(const char **id) {
157         return 1;
158 }
159
160 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
161         assert_se(index == test_index);
162
163         if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
164                 return -errno;
165
166         return test_dhcp_fd[0];
167 }
168
169 static int verify_solicit(DHCP6Message *solicit, uint8_t *option, size_t len) {
170         uint8_t *optval;
171         uint16_t optcode;
172         size_t optlen;
173         bool found_clientid = false, found_iana = false;
174         int r;
175
176         assert_se(solicit->type == DHCP6_SOLICIT);
177
178         while ((r = dhcp6_option_parse(&option, &len,
179                                        &optcode, &optlen, &optval)) >= 0) {
180                 switch(optcode) {
181                 case DHCP6_OPTION_CLIENTID:
182                         assert_se(!found_clientid);
183                         found_clientid = true;
184
185                         assert_se(optlen == 14);
186
187                         break;
188
189                 case DHCP6_OPTION_IA_NA:
190                         assert_se(!found_iana);
191                         found_iana = true;
192
193                         assert_se(optlen == 12);
194
195                         break;
196                 }
197         }
198
199         assert_se(r == -ENOMSG);
200         assert_se(found_clientid && found_iana);
201
202         sd_event_exit(e_solicit, 0);
203
204         return 0;
205 }
206
207 int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
208                                   const void *packet, size_t len) {
209         struct in6_addr mcast =
210                 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
211         DHCP6Message *message;
212         uint8_t *option;
213
214         assert_se(s == test_dhcp_fd[0]);
215         assert_se(server_address);
216         assert_se(packet);
217         assert_se(len > sizeof(DHCP6Message) + 4);
218
219         assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
220
221         message = (DHCP6Message *)packet;
222         option = (uint8_t *)(message + 1);
223         len -= sizeof(DHCP6Message);
224
225         assert_se(message->transaction_id & 0x00ffffff);
226
227         verify_solicit(message, option, len);
228
229         return len;
230 }
231
232 static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
233                                    void *userdata) {
234         sd_event *e = userdata;
235
236         assert_se(e);
237
238         if (verbose)
239                 printf("  got DHCPv6 event %d\n", event);
240
241         sd_event_exit(e, 0);
242 }
243
244 static int test_client_solicit(sd_event *e) {
245         sd_dhcp6_client *client;
246         usec_t time_now = now(CLOCK_MONOTONIC);
247
248         if (verbose)
249                 printf("* %s\n", __FUNCTION__);
250
251         assert_se(sd_dhcp6_client_new(&client) >= 0);
252         assert_se(client);
253
254         assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
255
256         assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
257         assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
258
259         assert_se(sd_dhcp6_client_set_callback(client,
260                                                test_client_solicit_cb, e) >= 0);
261
262         assert_se(sd_event_add_time(e, &hangcheck, CLOCK_MONOTONIC,
263                                     time_now + 2 * USEC_PER_SEC, 0,
264                                     test_hangcheck, NULL) >= 0);
265
266         e_solicit = e;
267
268         assert_se(sd_dhcp6_client_start(client) >= 0);
269
270         sd_event_loop(e);
271
272         hangcheck = sd_event_source_unref(hangcheck);
273
274         assert_se(!sd_dhcp6_client_unref(client));
275
276         test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
277
278         return 0;
279 }
280
281 int main(int argc, char *argv[]) {
282         _cleanup_event_unref_ sd_event *e;
283
284         assert_se(sd_event_new(&e) >= 0);
285
286         log_set_max_level(LOG_DEBUG);
287         log_parse_environment();
288         log_open();
289
290         test_client_basic(e);
291         test_option(e);
292         test_client_solicit(e);
293
294         assert_se(!sd_event_unref(e));
295
296         return 0;
297 }