chiark / gitweb /
libsystemd-network: Add hangcheck timer for DHCP client test
[elogind.git] / src / libsystemd-network / test-dhcp-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) 2013 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 <stdlib.h>
23 #include <assert.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <unistd.h>
29
30 #include "util.h"
31 #include "socket-util.h"
32
33 #include "dhcp-protocol.h"
34 #include "dhcp-internal.h"
35 #include "sd-dhcp-client.h"
36
37 static struct ether_addr mac_addr = {
38         .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
39 };
40
41 typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp);
42
43 static bool verbose = false;
44 static int test_fd[2];
45 static test_callback_recv_t callback_recv;
46 static be32_t xid;
47 static sd_event_source *test_hangcheck;
48
49 static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec,
50                                void *userdata)
51 {
52         assert_not_reached("Test case should have completed in 2 seconds");
53
54         return 0;
55 }
56
57 static void test_request_basic(sd_event *e)
58 {
59         int r;
60
61         sd_dhcp_client *client;
62
63         if (verbose)
64                 printf("* %s\n", __FUNCTION__);
65
66         r = sd_dhcp_client_new(&client);
67
68         assert_se(r >= 0);
69         assert_se(client);
70
71         r = sd_dhcp_client_attach_event(client, e, 0);
72         assert_se(r >= 0);
73
74         assert_se(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
75         assert_se(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
76         assert_se(sd_dhcp_client_set_index(NULL, 0) == -EINVAL);
77
78         assert_se(sd_dhcp_client_set_index(client, 15) == 0);
79         assert_se(sd_dhcp_client_set_index(client, -42) == -EINVAL);
80         assert_se(sd_dhcp_client_set_index(client, -1) == 0);
81
82         assert_se(sd_dhcp_client_set_request_option(client,
83                                         DHCP_OPTION_SUBNET_MASK) == -EEXIST);
84         assert_se(sd_dhcp_client_set_request_option(client,
85                                         DHCP_OPTION_ROUTER) == -EEXIST);
86         assert_se(sd_dhcp_client_set_request_option(client,
87                                         DHCP_OPTION_HOST_NAME) == -EEXIST);
88         assert_se(sd_dhcp_client_set_request_option(client,
89                                         DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
90         assert_se(sd_dhcp_client_set_request_option(client,
91                                         DHCP_OPTION_DOMAIN_NAME_SERVER)
92                         == -EEXIST);
93         assert_se(sd_dhcp_client_set_request_option(client,
94                                         DHCP_OPTION_NTP_SERVER) == -EEXIST);
95
96         assert_se(sd_dhcp_client_set_request_option(client,
97                                         DHCP_OPTION_PAD) == -EINVAL);
98         assert_se(sd_dhcp_client_set_request_option(client,
99                                         DHCP_OPTION_END) == -EINVAL);
100         assert_se(sd_dhcp_client_set_request_option(client,
101                                         DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
102         assert_se(sd_dhcp_client_set_request_option(client,
103                                         DHCP_OPTION_OVERLOAD) == -EINVAL);
104         assert_se(sd_dhcp_client_set_request_option(client,
105                                         DHCP_OPTION_PARAMETER_REQUEST_LIST)
106                         == -EINVAL);
107
108         assert_se(sd_dhcp_client_set_request_option(client, 33) == 0);
109         assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
110         assert_se(sd_dhcp_client_set_request_option(client, 44) == 0);
111         assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
112 }
113
114 static void test_checksum(void)
115 {
116         uint8_t buf[20] = {
117                 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
118                 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119                 0xff, 0xff, 0xff, 0xff
120         };
121
122         if (verbose)
123                 printf("* %s\n", __FUNCTION__);
124
125         assert_se(dhcp_packet_checksum(&buf, 20) == be16toh(0x78ae));
126 }
127
128 static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
129                 void *user_data)
130 {
131         return 0;
132 }
133
134 int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
135                                  const void *packet, size_t len)
136 {
137         size_t size;
138         _cleanup_free_ DHCPPacket *discover;
139         uint16_t ip_check, udp_check;
140
141         assert_se(s >= 0);
142         assert_se(packet);
143
144         size = sizeof(DHCPPacket) + 4;
145         assert_se(len > size);
146
147         discover = memdup(packet, len);
148
149         assert_se(discover->ip.ttl == IPDEFTTL);
150         assert_se(discover->ip.protocol == IPPROTO_UDP);
151         assert_se(discover->ip.saddr == INADDR_ANY);
152         assert_se(discover->ip.daddr == INADDR_BROADCAST);
153         assert_se(discover->udp.source == be16toh(DHCP_PORT_CLIENT));
154         assert_se(discover->udp.dest == be16toh(DHCP_PORT_SERVER));
155
156         ip_check = discover->ip.check;
157
158         discover->ip.ttl = 0;
159         discover->ip.check = discover->udp.len;
160
161         udp_check = ~dhcp_packet_checksum(&discover->ip.ttl, len - 8);
162         assert_se(udp_check == 0xffff);
163
164         discover->ip.ttl = IPDEFTTL;
165         discover->ip.check = ip_check;
166
167         ip_check = ~dhcp_packet_checksum(&discover->ip, sizeof(discover->ip));
168         assert_se(ip_check == 0xffff);
169
170         assert_se(discover->dhcp.xid);
171         assert_se(memcmp(discover->dhcp.chaddr,
172                       &mac_addr.ether_addr_octet, 6) == 0);
173
174         size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
175
176         assert_se(callback_recv);
177         callback_recv(size, &discover->dhcp);
178
179         return 575;
180 }
181
182 int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
183 {
184         if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
185                 return -errno;
186
187         return test_fd[0];
188 }
189
190 int dhcp_network_bind_udp_socket(int index, be32_t address, uint16_t port)
191 {
192         return 0;
193 }
194
195 int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
196                                  const void *packet, size_t len)
197 {
198         return 0;
199 }
200
201 static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp)
202 {
203         int res;
204
205         res = dhcp_option_parse(dhcp, size, check_options, NULL);
206         assert_se(res == DHCP_DISCOVER);
207
208         if (verbose)
209                 printf("  recv DHCP Discover 0x%08x\n", be32toh(dhcp->xid));
210
211         return 0;
212 }
213
214 static void test_discover_message(sd_event *e)
215 {
216         sd_dhcp_client *client;
217         int res, r;
218
219         if (verbose)
220                 printf("* %s\n", __FUNCTION__);
221
222         r = sd_dhcp_client_new(&client);
223         assert_se(r >= 0);
224         assert_se(client);
225
226         r = sd_dhcp_client_attach_event(client, e, 0);
227         assert_se(r >= 0);
228
229         assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
230         assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
231
232         assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0);
233
234         callback_recv = test_discover_message_verify;
235
236         res = sd_dhcp_client_start(client);
237
238         assert_se(res == 0 || res == -EINPROGRESS);
239
240         sd_event_run(e, (uint64_t) -1);
241
242         sd_dhcp_client_stop(client);
243         sd_dhcp_client_free(client);
244
245         close(test_fd[0]);
246         close(test_fd[1]);
247
248         callback_recv = NULL;
249 }
250
251 static uint8_t test_addr_acq_offer[] = {
252         0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
253         0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
254         0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
255         0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
256         0x6f, 0x95, 0x2f, 0x30, 0x00, 0x00, 0x00, 0x00,
257         0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
258         0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
259         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
260         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
261         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
262         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
263         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
264         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
265         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
266         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
267         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
268         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
269         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
270         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
271         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
272         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
273         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
274         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
275         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
276         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
277         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
278         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
279         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
280         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
281         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
282         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
283         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
284         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
285         0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x36,
286         0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
287         0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff,
288         0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
289         0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
290         0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
291         0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
292         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
293 };
294
295 static uint8_t test_addr_acq_ack[] = {
296         0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
297         0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
298         0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
299         0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
300         0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
301         0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
302         0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
303         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
304         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
305         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
306         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
310         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
311         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
312         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
313         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
314         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
315         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
316         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
317         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
318         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
319         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
320         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
322         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
323         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
324         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
327         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329         0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,
330         0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
331         0x00, 0x02, 0x58, 0x01, 0x04,   0xff, 0xff, 0xff,
332         0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
333         0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
334         0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
335         0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
336         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
337 };
338
339 static void test_addr_acq_acquired(sd_dhcp_client *client, int event,
340                                    void *userdata)
341 {
342         sd_event *e = userdata;
343         sd_dhcp_lease *lease;
344         struct in_addr addr;
345
346         assert_se(client);
347         assert_se(event == DHCP_EVENT_IP_ACQUIRE);
348
349         assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0);
350         assert_se(lease);
351
352         assert_se(sd_dhcp_lease_get_address(lease, &addr) >= 0);
353         assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[44],
354                       sizeof(addr.s_addr)) == 0);
355
356         assert_se(sd_dhcp_lease_get_netmask(lease, &addr) >= 0);
357         assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[285],
358                       sizeof(addr.s_addr)) == 0);
359
360         assert_se(sd_dhcp_lease_get_router(lease, &addr) >= 0);
361         assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[308],
362                       sizeof(addr.s_addr)) == 0);
363
364         if (verbose)
365                 printf("  DHCP address acquired\n");
366
367         sd_event_exit(e, 0);
368 }
369
370 static int test_addr_acq_recv_request(size_t size, DHCPMessage *request)
371 {
372         uint16_t udp_check = 0;
373         int res;
374
375         res = dhcp_option_parse(request, size, check_options, NULL);
376         assert_se(res == DHCP_REQUEST);
377         assert_se(xid == request->xid);
378
379         if (verbose)
380                 printf("  recv DHCP Request  0x%08x\n", be32toh(xid));
381
382         memcpy(&test_addr_acq_ack[26], &udp_check, sizeof(udp_check));
383         memcpy(&test_addr_acq_ack[32], &xid, sizeof(xid));
384         memcpy(&test_addr_acq_ack[56], &mac_addr.ether_addr_octet,
385                ETHER_ADDR_LEN);
386
387         callback_recv = NULL;
388
389         res = write(test_fd[1], test_addr_acq_ack,
390                     sizeof(test_addr_acq_ack));
391         assert_se(res == sizeof(test_addr_acq_ack));
392
393         if (verbose)
394                 printf("  send DHCP Ack\n");
395
396         return 0;
397 };
398
399 static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover)
400 {
401         uint16_t udp_check = 0;
402         int res;
403
404         res = dhcp_option_parse(discover, size, check_options, NULL);
405         assert_se(res == DHCP_DISCOVER);
406
407         xid = discover->xid;
408
409         if (verbose)
410                 printf("  recv DHCP Discover 0x%08x\n", be32toh(xid));
411
412         memcpy(&test_addr_acq_offer[26], &udp_check, sizeof(udp_check));
413         memcpy(&test_addr_acq_offer[32], &xid, sizeof(xid));
414         memcpy(&test_addr_acq_offer[56], &mac_addr.ether_addr_octet,
415                ETHER_ADDR_LEN);
416
417         callback_recv = test_addr_acq_recv_request;
418
419         res = write(test_fd[1], test_addr_acq_offer,
420                     sizeof(test_addr_acq_offer));
421         assert_se(res == sizeof(test_addr_acq_offer));
422
423         if (verbose)
424                 printf("  send DHCP Offer\n");
425
426         return 0;
427 }
428
429 static void test_addr_acq(sd_event *e)
430 {
431         usec_t time_now = now(CLOCK_MONOTONIC);
432         sd_dhcp_client *client;
433         int res, r;
434
435         if (verbose)
436                 printf("* %s\n", __FUNCTION__);
437
438         r = sd_dhcp_client_new(&client);
439         assert_se(r >= 0);
440         assert_se(client);
441
442         r = sd_dhcp_client_attach_event(client, e, 0);
443         assert_se(r >= 0);
444
445         assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
446         assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
447
448         assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e)
449                 >= 0);
450
451         callback_recv = test_addr_acq_recv_discover;
452
453         assert_se(sd_event_add_monotonic(e, &test_hangcheck,
454                                          time_now + 2 * USEC_PER_SEC, 0,
455                                          test_dhcp_hangcheck, NULL) >= 0);
456
457         res = sd_dhcp_client_start(client);
458         assert_se(res == 0 || res == -EINPROGRESS);
459
460         sd_event_loop(e);
461
462         test_hangcheck = sd_event_source_unref(test_hangcheck);
463
464         sd_dhcp_client_set_callback(client, NULL, NULL);
465         sd_dhcp_client_stop(client);
466         sd_dhcp_client_free(client);
467
468         close(test_fd[0]);
469         close(test_fd[1]);
470
471         callback_recv = NULL;
472         xid = 0;
473 }
474
475 int main(int argc, char *argv[])
476 {
477         sd_event *e;
478
479         assert_se(sd_event_new(&e) >= 0);
480
481         test_request_basic(e);
482         test_checksum();
483
484         test_discover_message(e);
485         test_addr_acq(e);
486
487         return 0;
488 }