]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/test-dhcp-client.c
sd-dhcp-client: Add reference counting for DHCP
[thirdparty/systemd.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);
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, uint32_t id)
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_unref(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 sd_event *e = userdata;
352 sd_dhcp_lease *lease;
353 struct in_addr addr;
354
355 assert_se(client);
356 assert_se(event == DHCP_EVENT_IP_ACQUIRE);
357
358 assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0);
359 assert_se(lease);
360
361 assert_se(sd_dhcp_lease_get_address(lease, &addr) >= 0);
362 assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[44],
363 sizeof(addr.s_addr)) == 0);
364
365 assert_se(sd_dhcp_lease_get_netmask(lease, &addr) >= 0);
366 assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[285],
367 sizeof(addr.s_addr)) == 0);
368
369 assert_se(sd_dhcp_lease_get_router(lease, &addr) >= 0);
370 assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[308],
371 sizeof(addr.s_addr)) == 0);
372
373 if (verbose)
374 printf(" DHCP address acquired\n");
375
376 sd_event_exit(e, 0);
377 }
378
379 static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) {
380 uint16_t udp_check = 0;
381 uint8_t *msg_bytes = (uint8_t *)request;
382 int res;
383
384 res = dhcp_option_parse(request, size, check_options, NULL);
385 assert_se(res == DHCP_REQUEST);
386 assert_se(xid == request->xid);
387
388 assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
389
390 if (verbose)
391 printf(" recv DHCP Request 0x%08x\n", be32toh(xid));
392
393 memcpy(&test_addr_acq_ack[26], &udp_check, sizeof(udp_check));
394 memcpy(&test_addr_acq_ack[32], &xid, sizeof(xid));
395 memcpy(&test_addr_acq_ack[56], &mac_addr.ether_addr_octet,
396 ETHER_ADDR_LEN);
397
398 callback_recv = NULL;
399
400 res = write(test_fd[1], test_addr_acq_ack,
401 sizeof(test_addr_acq_ack));
402 assert_se(res == sizeof(test_addr_acq_ack));
403
404 if (verbose)
405 printf(" send DHCP Ack\n");
406
407 return 0;
408 };
409
410 static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) {
411 uint16_t udp_check = 0;
412 uint8_t *msg_bytes = (uint8_t *)discover;
413 int res;
414
415 res = dhcp_option_parse(discover, size, check_options, NULL);
416 assert_se(res == DHCP_DISCOVER);
417
418 assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
419
420 xid = discover->xid;
421
422 if (verbose)
423 printf(" recv DHCP Discover 0x%08x\n", be32toh(xid));
424
425 memcpy(&test_addr_acq_offer[26], &udp_check, sizeof(udp_check));
426 memcpy(&test_addr_acq_offer[32], &xid, sizeof(xid));
427 memcpy(&test_addr_acq_offer[56], &mac_addr.ether_addr_octet,
428 ETHER_ADDR_LEN);
429
430 callback_recv = test_addr_acq_recv_request;
431
432 res = write(test_fd[1], test_addr_acq_offer,
433 sizeof(test_addr_acq_offer));
434 assert_se(res == sizeof(test_addr_acq_offer));
435
436 if (verbose)
437 printf(" sent DHCP Offer\n");
438
439 return 0;
440 }
441
442 static void test_addr_acq(sd_event *e) {
443 usec_t time_now = now(CLOCK_MONOTONIC);
444 sd_dhcp_client *client;
445 int res, r;
446
447 if (verbose)
448 printf("* %s\n", __FUNCTION__);
449
450 r = sd_dhcp_client_new(&client);
451 assert_se(r >= 0);
452 assert_se(client);
453
454 r = sd_dhcp_client_attach_event(client, e, 0);
455 assert_se(r >= 0);
456
457 assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
458 assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
459
460 assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e)
461 >= 0);
462
463 callback_recv = test_addr_acq_recv_discover;
464
465 assert_se(sd_event_add_time(e, &test_hangcheck,
466 CLOCK_MONOTONIC,
467 time_now + 2 * USEC_PER_SEC, 0,
468 test_dhcp_hangcheck, NULL) >= 0);
469
470 res = sd_dhcp_client_start(client);
471 assert_se(res == 0 || res == -EINPROGRESS);
472
473 sd_event_loop(e);
474
475 test_hangcheck = sd_event_source_unref(test_hangcheck);
476
477 sd_dhcp_client_set_callback(client, NULL, NULL);
478 sd_dhcp_client_stop(client);
479 sd_dhcp_client_unref(client);
480
481 test_fd[1] = safe_close(test_fd[1]);
482
483 callback_recv = NULL;
484 xid = 0;
485 }
486
487 int main(int argc, char *argv[]) {
488 sd_event *e;
489
490 assert_se(sd_event_new(&e) >= 0);
491
492 test_request_basic(e);
493 test_checksum();
494
495 test_discover_message(e);
496 test_addr_acq(e);
497
498 return 0;
499 }