1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2013 Intel Corporation. All rights reserved.
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.
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.
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/>.
24 #include <sys/socket.h>
27 #include "sd-dhcp-client.h"
30 #include "alloc-util.h"
31 #include "dhcp-identifier.h"
32 #include "dhcp-internal.h"
33 #include "dhcp-protocol.h"
37 static uint8_t mac_addr
[] = {'A', 'B', 'C', '1', '2', '3'};
39 typedef int (*test_callback_recv_t
)(size_t size
, DHCPMessage
*dhcp
);
41 static bool verbose
= true;
42 static int test_fd
[2];
43 static test_callback_recv_t callback_recv
;
45 static sd_event_source
*test_hangcheck
;
47 static int test_dhcp_hangcheck(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
48 assert_not_reached("Test case should have completed in 2 seconds");
53 static void test_request_basic(sd_event
*e
) {
56 sd_dhcp_client
*client
;
59 printf("* %s\n", __FUNCTION__
);
61 r
= sd_dhcp_client_new(&client
);
66 r
= sd_dhcp_client_attach_event(client
, e
, 0);
69 assert_se(sd_dhcp_client_set_request_option(NULL
, 0) == -EINVAL
);
70 assert_se(sd_dhcp_client_set_request_address(NULL
, NULL
) == -EINVAL
);
71 assert_se(sd_dhcp_client_set_index(NULL
, 0) == -EINVAL
);
73 assert_se(sd_dhcp_client_set_index(client
, 15) == 0);
74 assert_se(sd_dhcp_client_set_index(client
, -42) == -EINVAL
);
75 assert_se(sd_dhcp_client_set_index(client
, -1) == -EINVAL
);
76 assert_se(sd_dhcp_client_set_index(client
, 0) == -EINVAL
);
77 assert_se(sd_dhcp_client_set_index(client
, 1) == 0);
79 assert_se(sd_dhcp_client_set_request_option(client
,
80 SD_DHCP_OPTION_SUBNET_MASK
) == -EEXIST
);
81 assert_se(sd_dhcp_client_set_request_option(client
,
82 SD_DHCP_OPTION_ROUTER
) == -EEXIST
);
83 assert_se(sd_dhcp_client_set_request_option(client
,
84 SD_DHCP_OPTION_HOST_NAME
) == -EEXIST
);
85 assert_se(sd_dhcp_client_set_request_option(client
,
86 SD_DHCP_OPTION_DOMAIN_NAME
) == -EEXIST
);
87 assert_se(sd_dhcp_client_set_request_option(client
,
88 SD_DHCP_OPTION_DOMAIN_NAME_SERVER
) == -EEXIST
);
90 assert_se(sd_dhcp_client_set_request_option(client
,
91 SD_DHCP_OPTION_PAD
) == -EINVAL
);
92 assert_se(sd_dhcp_client_set_request_option(client
,
93 SD_DHCP_OPTION_END
) == -EINVAL
);
94 assert_se(sd_dhcp_client_set_request_option(client
,
95 SD_DHCP_OPTION_MESSAGE_TYPE
) == -EINVAL
);
96 assert_se(sd_dhcp_client_set_request_option(client
,
97 SD_DHCP_OPTION_OVERLOAD
) == -EINVAL
);
98 assert_se(sd_dhcp_client_set_request_option(client
,
99 SD_DHCP_OPTION_PARAMETER_REQUEST_LIST
)
102 assert_se(sd_dhcp_client_set_request_option(client
, 33) == 0);
103 assert_se(sd_dhcp_client_set_request_option(client
, 33) == -EEXIST
);
104 assert_se(sd_dhcp_client_set_request_option(client
, 44) == 0);
105 assert_se(sd_dhcp_client_set_request_option(client
, 33) == -EEXIST
);
107 sd_dhcp_client_unref(client
);
110 static void test_checksum(void) {
112 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
113 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 0xff, 0xff, 0xff, 0xff
118 printf("* %s\n", __FUNCTION__
);
120 assert_se(dhcp_packet_checksum((uint8_t*)&buf
, 20) == be16toh(0x78ae));
123 static int check_options(uint8_t code
, uint8_t len
, const void *option
, void *userdata
) {
125 case SD_DHCP_OPTION_CLIENT_IDENTIFIER
:
131 assert_se(dhcp_identifier_set_duid_en(&duid
, &duid_len
) >= 0);
132 assert_se(dhcp_identifier_set_iaid(42, mac_addr
, ETH_ALEN
, &iaid
) >= 0);
134 assert_se(len
== sizeof(uint8_t) + sizeof(uint32_t) + duid_len
);
135 assert_se(len
== 19);
136 assert_se(((uint8_t*) option
)[0] == 0xff);
138 assert_se(memcmp((uint8_t*) option
+ 1, &iaid
, sizeof(iaid
)) == 0);
139 assert_se(memcmp((uint8_t*) option
+ 5, &duid
, duid_len
) == 0);
150 int dhcp_network_send_raw_socket(int s
, const union sockaddr_union
*link
, const void *packet
, size_t len
) {
152 _cleanup_free_ DHCPPacket
*discover
;
153 uint16_t ip_check
, udp_check
;
158 size
= sizeof(DHCPPacket
);
159 assert_se(len
> size
);
161 discover
= memdup(packet
, len
);
163 assert_se(discover
->ip
.ttl
== IPDEFTTL
);
164 assert_se(discover
->ip
.protocol
== IPPROTO_UDP
);
165 assert_se(discover
->ip
.saddr
== INADDR_ANY
);
166 assert_se(discover
->ip
.daddr
== INADDR_BROADCAST
);
167 assert_se(discover
->udp
.source
== be16toh(DHCP_PORT_CLIENT
));
168 assert_se(discover
->udp
.dest
== be16toh(DHCP_PORT_SERVER
));
170 ip_check
= discover
->ip
.check
;
172 discover
->ip
.ttl
= 0;
173 discover
->ip
.check
= discover
->udp
.len
;
175 udp_check
= ~dhcp_packet_checksum((uint8_t*)&discover
->ip
.ttl
, len
- 8);
176 assert_se(udp_check
== 0xffff);
178 discover
->ip
.ttl
= IPDEFTTL
;
179 discover
->ip
.check
= ip_check
;
181 ip_check
= ~dhcp_packet_checksum((uint8_t*)&discover
->ip
, sizeof(discover
->ip
));
182 assert_se(ip_check
== 0xffff);
184 assert_se(discover
->dhcp
.xid
);
185 assert_se(memcmp(discover
->dhcp
.chaddr
, &mac_addr
, ETH_ALEN
) == 0);
187 size
= len
- sizeof(struct iphdr
) - sizeof(struct udphdr
);
189 assert_se(callback_recv
);
190 callback_recv(size
, &discover
->dhcp
);
195 int dhcp_network_bind_raw_socket(
197 union sockaddr_union
*link
,
199 const uint8_t *addr
, size_t addr_len
,
202 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, test_fd
) < 0)
208 int dhcp_network_bind_udp_socket(be32_t address
, uint16_t port
) {
211 fd
= socket(AF_INET
, SOCK_DGRAM
|SOCK_CLOEXEC
, 0);
218 int dhcp_network_send_udp_socket(int s
, be32_t address
, uint16_t port
, const void *packet
, size_t len
) {
222 static int test_discover_message_verify(size_t size
, struct DHCPMessage
*dhcp
) {
225 res
= dhcp_option_parse(dhcp
, size
, check_options
, NULL
, NULL
);
226 assert_se(res
== DHCP_DISCOVER
);
229 printf(" recv DHCP Discover 0x%08x\n", be32toh(dhcp
->xid
));
234 static void test_discover_message(sd_event
*e
) {
235 sd_dhcp_client
*client
;
239 printf("* %s\n", __FUNCTION__
);
241 r
= sd_dhcp_client_new(&client
);
245 r
= sd_dhcp_client_attach_event(client
, e
, 0);
248 assert_se(sd_dhcp_client_set_index(client
, 42) >= 0);
249 assert_se(sd_dhcp_client_set_mac(client
, mac_addr
, ETH_ALEN
, ARPHRD_ETHER
) >= 0);
251 assert_se(sd_dhcp_client_set_request_option(client
, 248) >= 0);
253 callback_recv
= test_discover_message_verify
;
255 res
= sd_dhcp_client_start(client
);
257 assert_se(res
== 0 || res
== -EINPROGRESS
);
259 sd_event_run(e
, (uint64_t) -1);
261 sd_dhcp_client_stop(client
);
262 sd_dhcp_client_unref(client
);
264 test_fd
[1] = safe_close(test_fd
[1]);
266 callback_recv
= NULL
;
269 static uint8_t test_addr_acq_offer
[] = {
270 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
271 0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
272 0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
273 0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
274 0x6f, 0x95, 0x2f, 0x30, 0x00, 0x00, 0x00, 0x00,
275 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
276 0xc0, 0xa8, 0x02, 0x01, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
302 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
303 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x36,
304 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
305 0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff,
306 0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
307 0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
308 0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
309 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
310 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
313 static uint8_t test_addr_acq_ack
[] = {
314 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
315 0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
316 0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
317 0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
318 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
319 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
320 0xc0, 0xa8, 0x02, 0x01, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
347 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,
348 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
349 0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff,
350 0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
351 0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
352 0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
353 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
354 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
357 static void test_addr_acq_acquired(sd_dhcp_client
*client
, int event
,
359 sd_event
*e
= userdata
;
360 sd_dhcp_lease
*lease
;
364 assert_se(event
== SD_DHCP_CLIENT_EVENT_IP_ACQUIRE
);
366 assert_se(sd_dhcp_client_get_lease(client
, &lease
) >= 0);
369 assert_se(sd_dhcp_lease_get_address(lease
, &addr
) >= 0);
370 assert_se(memcmp(&addr
.s_addr
, &test_addr_acq_ack
[44],
371 sizeof(addr
.s_addr
)) == 0);
373 assert_se(sd_dhcp_lease_get_netmask(lease
, &addr
) >= 0);
374 assert_se(memcmp(&addr
.s_addr
, &test_addr_acq_ack
[285],
375 sizeof(addr
.s_addr
)) == 0);
377 assert_se(sd_dhcp_lease_get_router(lease
, &addr
) >= 0);
378 assert_se(memcmp(&addr
.s_addr
, &test_addr_acq_ack
[308],
379 sizeof(addr
.s_addr
)) == 0);
382 printf(" DHCP address acquired\n");
387 static int test_addr_acq_recv_request(size_t size
, DHCPMessage
*request
) {
388 uint16_t udp_check
= 0;
389 uint8_t *msg_bytes
= (uint8_t *)request
;
392 res
= dhcp_option_parse(request
, size
, check_options
, NULL
, NULL
);
393 assert_se(res
== DHCP_REQUEST
);
394 assert_se(xid
== request
->xid
);
396 assert_se(msg_bytes
[size
- 1] == SD_DHCP_OPTION_END
);
399 printf(" recv DHCP Request 0x%08x\n", be32toh(xid
));
401 memcpy(&test_addr_acq_ack
[26], &udp_check
, sizeof(udp_check
));
402 memcpy(&test_addr_acq_ack
[32], &xid
, sizeof(xid
));
403 memcpy(&test_addr_acq_ack
[56], &mac_addr
, ETHER_ADDR_LEN
);
405 callback_recv
= NULL
;
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
));
412 printf(" send DHCP Ack\n");
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
;
422 res
= dhcp_option_parse(discover
, size
, check_options
, NULL
, NULL
);
423 assert_se(res
== DHCP_DISCOVER
);
425 assert_se(msg_bytes
[size
- 1] == SD_DHCP_OPTION_END
);
430 printf(" recv DHCP Discover 0x%08x\n", be32toh(xid
));
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_LEN
);
436 callback_recv
= test_addr_acq_recv_request
;
438 res
= write(test_fd
[1], test_addr_acq_offer
,
439 sizeof(test_addr_acq_offer
));
440 assert_se(res
== sizeof(test_addr_acq_offer
));
443 printf(" sent DHCP Offer\n");
448 static void test_addr_acq(sd_event
*e
) {
449 usec_t time_now
= now(clock_boottime_or_monotonic());
450 sd_dhcp_client
*client
;
454 printf("* %s\n", __FUNCTION__
);
456 r
= sd_dhcp_client_new(&client
);
460 r
= sd_dhcp_client_attach_event(client
, e
, 0);
463 assert_se(sd_dhcp_client_set_index(client
, 42) >= 0);
464 assert_se(sd_dhcp_client_set_mac(client
, mac_addr
, ETH_ALEN
, ARPHRD_ETHER
) >= 0);
466 assert_se(sd_dhcp_client_set_callback(client
, test_addr_acq_acquired
, e
) >= 0);
468 callback_recv
= test_addr_acq_recv_discover
;
470 assert_se(sd_event_add_time(e
, &test_hangcheck
,
471 clock_boottime_or_monotonic(),
472 time_now
+ 2 * USEC_PER_SEC
, 0,
473 test_dhcp_hangcheck
, NULL
) >= 0);
475 res
= sd_dhcp_client_start(client
);
476 assert_se(res
== 0 || res
== -EINPROGRESS
);
478 assert_se(sd_event_loop(e
) >= 0);
480 test_hangcheck
= sd_event_source_unref(test_hangcheck
);
482 assert_se(sd_dhcp_client_set_callback(client
, NULL
, NULL
) >= 0);
483 assert_se(sd_dhcp_client_stop(client
) >= 0);
484 sd_dhcp_client_unref(client
);
486 test_fd
[1] = safe_close(test_fd
[1]);
488 callback_recv
= NULL
;
492 int main(int argc
, char *argv
[]) {
493 _cleanup_(sd_event_unrefp
) sd_event
*e
;
495 log_set_max_level(LOG_DEBUG
);
496 log_parse_environment();
499 assert_se(sd_event_new(&e
) >= 0);
501 test_request_basic(e
);
504 test_discover_message(e
);
508 /* Make sure the async_close thread has finished.
509 * valgrind would report some of the phread_* structures
510 * as not cleaned up properly. */