chiark / gitweb /
libsystemd-dhcp: Update client test case for client id and end option
[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         close(test_fd[0]);
257         close(test_fd[1]);
258
259         callback_recv = NULL;
260 }
261
262 static uint8_t test_addr_acq_offer[] = {
263         0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
264         0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
265         0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
266         0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
267         0x6f, 0x95, 0x2f, 0x30, 0x00, 0x00, 0x00, 0x00,
268         0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
269         0xc0, 0xa8, 0x02, 0x01, 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         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
296         0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x36,
297         0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
298         0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff,
299         0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
300         0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
301         0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
302         0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
303         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
304 };
305
306 static uint8_t test_addr_acq_ack[] = {
307         0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
308         0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
309         0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
310         0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
311         0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
312         0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
313         0xc0, 0xa8, 0x02, 0x01, 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         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340         0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,
341         0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
342         0x00, 0x02, 0x58, 0x01, 0x04,   0xff, 0xff, 0xff,
343         0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
344         0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
345         0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
346         0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
347         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
348 };
349
350 static void test_addr_acq_acquired(sd_dhcp_client *client, int event,
351                                    void *userdata)
352 {
353         sd_event *e = userdata;
354         sd_dhcp_lease *lease;
355         struct in_addr addr;
356
357         assert_se(client);
358         assert_se(event == DHCP_EVENT_IP_ACQUIRE);
359
360         assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0);
361         assert_se(lease);
362
363         assert_se(sd_dhcp_lease_get_address(lease, &addr) >= 0);
364         assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[44],
365                       sizeof(addr.s_addr)) == 0);
366
367         assert_se(sd_dhcp_lease_get_netmask(lease, &addr) >= 0);
368         assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[285],
369                       sizeof(addr.s_addr)) == 0);
370
371         assert_se(sd_dhcp_lease_get_router(lease, &addr) >= 0);
372         assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[308],
373                       sizeof(addr.s_addr)) == 0);
374
375         if (verbose)
376                 printf("  DHCP address acquired\n");
377
378         sd_event_exit(e, 0);
379 }
380
381 static int test_addr_acq_recv_request(size_t size, DHCPMessage *request)
382 {
383         uint16_t udp_check = 0;
384         uint8_t *msg_bytes = (uint8_t *)request;
385         int res;
386
387         res = dhcp_option_parse(request, size, check_options, NULL);
388         assert_se(res == DHCP_REQUEST);
389         assert_se(xid == request->xid);
390
391         assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
392
393         if (verbose)
394                 printf("  recv DHCP Request  0x%08x\n", be32toh(xid));
395
396         memcpy(&test_addr_acq_ack[26], &udp_check, sizeof(udp_check));
397         memcpy(&test_addr_acq_ack[32], &xid, sizeof(xid));
398         memcpy(&test_addr_acq_ack[56], &mac_addr.ether_addr_octet,
399                ETHER_ADDR_LEN);
400
401         callback_recv = NULL;
402
403         res = write(test_fd[1], test_addr_acq_ack,
404                     sizeof(test_addr_acq_ack));
405         assert_se(res == sizeof(test_addr_acq_ack));
406
407         if (verbose)
408                 printf("  send DHCP Ack\n");
409
410         return 0;
411 };
412
413 static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover)
414 {
415         uint16_t udp_check = 0;
416         uint8_t *msg_bytes = (uint8_t *)discover;
417         int res;
418
419         res = dhcp_option_parse(discover, size, check_options, NULL);
420         assert_se(res == DHCP_DISCOVER);
421
422         assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
423
424         xid = discover->xid;
425
426         if (verbose)
427                 printf("  recv DHCP Discover 0x%08x\n", be32toh(xid));
428
429         memcpy(&test_addr_acq_offer[26], &udp_check, sizeof(udp_check));
430         memcpy(&test_addr_acq_offer[32], &xid, sizeof(xid));
431         memcpy(&test_addr_acq_offer[56], &mac_addr.ether_addr_octet,
432                ETHER_ADDR_LEN);
433
434         callback_recv = test_addr_acq_recv_request;
435
436         res = write(test_fd[1], test_addr_acq_offer,
437                     sizeof(test_addr_acq_offer));
438         assert_se(res == sizeof(test_addr_acq_offer));
439
440         if (verbose)
441                 printf("  send DHCP Offer\n");
442
443         return 0;
444 }
445
446 static void test_addr_acq(sd_event *e)
447 {
448         usec_t time_now = now(CLOCK_MONOTONIC);
449         sd_dhcp_client *client;
450         int res, r;
451
452         if (verbose)
453                 printf("* %s\n", __FUNCTION__);
454
455         r = sd_dhcp_client_new(&client);
456         assert_se(r >= 0);
457         assert_se(client);
458
459         r = sd_dhcp_client_attach_event(client, e, 0);
460         assert_se(r >= 0);
461
462         assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
463         assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
464
465         assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e)
466                 >= 0);
467
468         callback_recv = test_addr_acq_recv_discover;
469
470         assert_se(sd_event_add_monotonic(e, &test_hangcheck,
471                                          time_now + 2 * USEC_PER_SEC, 0,
472                                          test_dhcp_hangcheck, NULL) >= 0);
473
474         res = sd_dhcp_client_start(client);
475         assert_se(res == 0 || res == -EINPROGRESS);
476
477         sd_event_loop(e);
478
479         test_hangcheck = sd_event_source_unref(test_hangcheck);
480
481         sd_dhcp_client_set_callback(client, NULL, NULL);
482         sd_dhcp_client_stop(client);
483         sd_dhcp_client_free(client);
484
485         close(test_fd[0]);
486         close(test_fd[1]);
487
488         callback_recv = NULL;
489         xid = 0;
490 }
491
492 int main(int argc, char *argv[])
493 {
494         sd_event *e;
495
496         assert_se(sd_event_new(&e) >= 0);
497
498         test_request_basic(e);
499         test_checksum();
500
501         test_discover_message(e);
502         test_addr_acq(e);
503
504         return 0;
505 }