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