chiark / gitweb /
0f7adac562978c099a95a1f83c825b0e30070620
[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         switch(code) {
132         case DHCP_OPTION_CLIENT_IDENTIFIER:
133                 assert_se(len == 7);
134                 assert_se(option[0] == 0x01);
135                 assert_se(memcmp(&option[1], &mac_addr, ETH_ALEN) == 0);
136                 break;
137
138         default:
139                 break;
140         }
141
142         return 0;
143 }
144
145 int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
146                                  const void *packet, size_t len)
147 {
148         size_t size;
149         _cleanup_free_ DHCPPacket *discover;
150         uint16_t ip_check, udp_check;
151
152         assert_se(s >= 0);
153         assert_se(packet);
154
155         size = sizeof(DHCPPacket) + 4;
156         assert_se(len > size);
157
158         discover = memdup(packet, len);
159
160         assert_se(discover->ip.ttl == IPDEFTTL);
161         assert_se(discover->ip.protocol == IPPROTO_UDP);
162         assert_se(discover->ip.saddr == INADDR_ANY);
163         assert_se(discover->ip.daddr == INADDR_BROADCAST);
164         assert_se(discover->udp.source == be16toh(DHCP_PORT_CLIENT));
165         assert_se(discover->udp.dest == be16toh(DHCP_PORT_SERVER));
166
167         ip_check = discover->ip.check;
168
169         discover->ip.ttl = 0;
170         discover->ip.check = discover->udp.len;
171
172         udp_check = ~dhcp_packet_checksum(&discover->ip.ttl, len - 8);
173         assert_se(udp_check == 0xffff);
174
175         discover->ip.ttl = IPDEFTTL;
176         discover->ip.check = ip_check;
177
178         ip_check = ~dhcp_packet_checksum(&discover->ip, sizeof(discover->ip));
179         assert_se(ip_check == 0xffff);
180
181         assert_se(discover->dhcp.xid);
182         assert_se(memcmp(discover->dhcp.chaddr,
183                       &mac_addr.ether_addr_octet, 6) == 0);
184
185         size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
186
187         assert_se(callback_recv);
188         callback_recv(size, &discover->dhcp);
189
190         return 575;
191 }
192
193 int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
194 {
195         if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
196                 return -errno;
197
198         return test_fd[0];
199 }
200
201 int dhcp_network_bind_udp_socket(int index, be32_t address, uint16_t port)
202 {
203         return 0;
204 }
205
206 int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
207                                  const void *packet, size_t len)
208 {
209         return 0;
210 }
211
212 static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp)
213 {
214         int res;
215
216         res = dhcp_option_parse(dhcp, size, check_options, NULL);
217         assert_se(res == DHCP_DISCOVER);
218
219         if (verbose)
220                 printf("  recv DHCP Discover 0x%08x\n", be32toh(dhcp->xid));
221
222         return 0;
223 }
224
225 static void test_discover_message(sd_event *e)
226 {
227         sd_dhcp_client *client;
228         int res, r;
229
230         if (verbose)
231                 printf("* %s\n", __FUNCTION__);
232
233         r = sd_dhcp_client_new(&client);
234         assert_se(r >= 0);
235         assert_se(client);
236
237         r = sd_dhcp_client_attach_event(client, e, 0);
238         assert_se(r >= 0);
239
240         assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
241         assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
242
243         assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0);
244
245         callback_recv = test_discover_message_verify;
246
247         res = sd_dhcp_client_start(client);
248
249         assert_se(res == 0 || res == -EINPROGRESS);
250
251         sd_event_run(e, (uint64_t) -1);
252
253         sd_dhcp_client_stop(client);
254         sd_dhcp_client_free(client);
255
256         test_fd[1] = safe_close(test_fd[1]);
257
258         callback_recv = NULL;
259 }
260
261 static uint8_t test_addr_acq_offer[] = {
262         0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
263         0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
264         0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
265         0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
266         0x6f, 0x95, 0x2f, 0x30, 0x00, 0x00, 0x00, 0x00,
267         0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
268         0xc0, 0xa8, 0x02, 0x01, 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         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         0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x36,
296         0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
297         0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff,
298         0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
299         0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
300         0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
301         0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
302         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
303 };
304
305 static uint8_t test_addr_acq_ack[] = {
306         0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
307         0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
308         0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
309         0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
310         0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
311         0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
312         0xc0, 0xa8, 0x02, 0x01, 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         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         0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,
340         0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
341         0x00, 0x02, 0x58, 0x01, 0x04,   0xff, 0xff, 0xff,
342         0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
343         0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
344         0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
345         0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
347 };
348
349 static void test_addr_acq_acquired(sd_dhcp_client *client, int event,
350                                    void *userdata)
351 {
352         sd_event *e = userdata;
353         sd_dhcp_lease *lease;
354         struct in_addr addr;
355
356         assert_se(client);
357         assert_se(event == DHCP_EVENT_IP_ACQUIRE);
358
359         assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0);
360         assert_se(lease);
361
362         assert_se(sd_dhcp_lease_get_address(lease, &addr) >= 0);
363         assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[44],
364                       sizeof(addr.s_addr)) == 0);
365
366         assert_se(sd_dhcp_lease_get_netmask(lease, &addr) >= 0);
367         assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[285],
368                       sizeof(addr.s_addr)) == 0);
369
370         assert_se(sd_dhcp_lease_get_router(lease, &addr) >= 0);
371         assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[308],
372                       sizeof(addr.s_addr)) == 0);
373
374         if (verbose)
375                 printf("  DHCP address acquired\n");
376
377         sd_event_exit(e, 0);
378 }
379
380 static int test_addr_acq_recv_request(size_t size, DHCPMessage *request)
381 {
382         uint16_t udp_check = 0;
383         uint8_t *msg_bytes = (uint8_t *)request;
384         int res;
385
386         res = dhcp_option_parse(request, size, check_options, NULL);
387         assert_se(res == DHCP_REQUEST);
388         assert_se(xid == request->xid);
389
390         assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
391
392         if (verbose)
393                 printf("  recv DHCP Request  0x%08x\n", be32toh(xid));
394
395         memcpy(&test_addr_acq_ack[26], &udp_check, sizeof(udp_check));
396         memcpy(&test_addr_acq_ack[32], &xid, sizeof(xid));
397         memcpy(&test_addr_acq_ack[56], &mac_addr.ether_addr_octet,
398                ETHER_ADDR_LEN);
399
400         callback_recv = NULL;
401
402         res = write(test_fd[1], test_addr_acq_ack,
403                     sizeof(test_addr_acq_ack));
404         assert_se(res == sizeof(test_addr_acq_ack));
405
406         if (verbose)
407                 printf("  send DHCP Ack\n");
408
409         return 0;
410 };
411
412 static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover)
413 {
414         uint16_t udp_check = 0;
415         uint8_t *msg_bytes = (uint8_t *)discover;
416         int res;
417
418         res = dhcp_option_parse(discover, size, check_options, NULL);
419         assert_se(res == DHCP_DISCOVER);
420
421         assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
422
423         xid = discover->xid;
424
425         if (verbose)
426                 printf("  recv DHCP Discover 0x%08x\n", be32toh(xid));
427
428         memcpy(&test_addr_acq_offer[26], &udp_check, sizeof(udp_check));
429         memcpy(&test_addr_acq_offer[32], &xid, sizeof(xid));
430         memcpy(&test_addr_acq_offer[56], &mac_addr.ether_addr_octet,
431                ETHER_ADDR_LEN);
432
433         callback_recv = test_addr_acq_recv_request;
434
435         res = write(test_fd[1], test_addr_acq_offer,
436                     sizeof(test_addr_acq_offer));
437         assert_se(res == sizeof(test_addr_acq_offer));
438
439         if (verbose)
440                 printf("  send DHCP Offer\n");
441
442         return 0;
443 }
444
445 static void test_addr_acq(sd_event *e)
446 {
447         usec_t time_now = now(CLOCK_MONOTONIC);
448         sd_dhcp_client *client;
449         int res, r;
450
451         if (verbose)
452                 printf("* %s\n", __FUNCTION__);
453
454         r = sd_dhcp_client_new(&client);
455         assert_se(r >= 0);
456         assert_se(client);
457
458         r = sd_dhcp_client_attach_event(client, e, 0);
459         assert_se(r >= 0);
460
461         assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
462         assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
463
464         assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e)
465                 >= 0);
466
467         callback_recv = test_addr_acq_recv_discover;
468
469         assert_se(sd_event_add_monotonic(e, &test_hangcheck,
470                                          time_now + 2 * USEC_PER_SEC, 0,
471                                          test_dhcp_hangcheck, NULL) >= 0);
472
473         res = sd_dhcp_client_start(client);
474         assert_se(res == 0 || res == -EINPROGRESS);
475
476         sd_event_loop(e);
477
478         test_hangcheck = sd_event_source_unref(test_hangcheck);
479
480         sd_dhcp_client_set_callback(client, NULL, NULL);
481         sd_dhcp_client_stop(client);
482         sd_dhcp_client_free(client);
483
484         test_fd[1] = safe_close(test_fd[1]);
485
486         callback_recv = NULL;
487         xid = 0;
488 }
489
490 int main(int argc, char *argv[])
491 {
492         sd_event *e;
493
494         assert_se(sd_event_new(&e) >= 0);
495
496         test_request_basic(e);
497         test_checksum();
498
499         test_discover_message(e);
500         test_addr_acq(e);
501
502         return 0;
503 }