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