2 This file is part of systemd.
4 Copyright (C) 2013 Intel Corporation. All rights reserved.
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/socket.h>
25 #include "sd-dhcp-client.h"
28 #include "alloc-util.h"
29 #include "dhcp-identifier.h"
30 #include "dhcp-internal.h"
31 #include "dhcp-protocol.h"
35 static uint8_t mac_addr
[] = {'A', 'B', 'C', '1', '2', '3'};
37 typedef int (*test_callback_recv_t
)(size_t size
, DHCPMessage
*dhcp
);
39 static bool verbose
= true;
40 static int test_fd
[2];
41 static test_callback_recv_t callback_recv
;
43 static sd_event_source
*test_hangcheck
;
45 static int test_dhcp_hangcheck(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
46 assert_not_reached("Test case should have completed in 2 seconds");
51 static void test_request_basic(sd_event
*e
) {
54 sd_dhcp_client
*client
;
57 printf("* %s\n", __FUNCTION__
);
59 r
= sd_dhcp_client_new(&client
);
64 r
= sd_dhcp_client_attach_event(client
, e
, 0);
67 assert_se(sd_dhcp_client_set_request_option(NULL
, 0) == -EINVAL
);
68 assert_se(sd_dhcp_client_set_request_address(NULL
, NULL
) == -EINVAL
);
69 assert_se(sd_dhcp_client_set_index(NULL
, 0) == -EINVAL
);
71 assert_se(sd_dhcp_client_set_index(client
, 15) == 0);
72 assert_se(sd_dhcp_client_set_index(client
, -42) == -EINVAL
);
73 assert_se(sd_dhcp_client_set_index(client
, -1) == -EINVAL
);
74 assert_se(sd_dhcp_client_set_index(client
, 0) == -EINVAL
);
75 assert_se(sd_dhcp_client_set_index(client
, 1) == 0);
77 assert_se(sd_dhcp_client_set_request_option(client
,
78 SD_DHCP_OPTION_SUBNET_MASK
) == -EEXIST
);
79 assert_se(sd_dhcp_client_set_request_option(client
,
80 SD_DHCP_OPTION_ROUTER
) == -EEXIST
);
81 assert_se(sd_dhcp_client_set_request_option(client
,
82 SD_DHCP_OPTION_HOST_NAME
) == -EEXIST
);
83 assert_se(sd_dhcp_client_set_request_option(client
,
84 SD_DHCP_OPTION_DOMAIN_NAME
) == -EEXIST
);
85 assert_se(sd_dhcp_client_set_request_option(client
,
86 SD_DHCP_OPTION_DOMAIN_NAME_SERVER
) == -EEXIST
);
88 assert_se(sd_dhcp_client_set_request_option(client
,
89 SD_DHCP_OPTION_PAD
) == -EINVAL
);
90 assert_se(sd_dhcp_client_set_request_option(client
,
91 SD_DHCP_OPTION_END
) == -EINVAL
);
92 assert_se(sd_dhcp_client_set_request_option(client
,
93 SD_DHCP_OPTION_MESSAGE_TYPE
) == -EINVAL
);
94 assert_se(sd_dhcp_client_set_request_option(client
,
95 SD_DHCP_OPTION_OVERLOAD
) == -EINVAL
);
96 assert_se(sd_dhcp_client_set_request_option(client
,
97 SD_DHCP_OPTION_PARAMETER_REQUEST_LIST
)
100 assert_se(sd_dhcp_client_set_request_option(client
, 33) == 0);
101 assert_se(sd_dhcp_client_set_request_option(client
, 33) == -EEXIST
);
102 assert_se(sd_dhcp_client_set_request_option(client
, 44) == 0);
103 assert_se(sd_dhcp_client_set_request_option(client
, 33) == -EEXIST
);
105 sd_dhcp_client_unref(client
);
108 static void test_checksum(void) {
110 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
111 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 0xff, 0xff, 0xff, 0xff
116 printf("* %s\n", __FUNCTION__
);
118 assert_se(dhcp_packet_checksum((uint8_t*)&buf
, 20) == be16toh(0x78ae));
121 static int check_options(uint8_t code
, uint8_t len
, const void *option
, void *userdata
) {
123 case SD_DHCP_OPTION_CLIENT_IDENTIFIER
:
129 assert_se(dhcp_identifier_set_duid_en(&duid
, &duid_len
) >= 0);
130 assert_se(dhcp_identifier_set_iaid(42, mac_addr
, ETH_ALEN
, &iaid
) >= 0);
132 assert_se(len
== sizeof(uint8_t) + sizeof(uint32_t) + duid_len
);
133 assert_se(len
== 19);
134 assert_se(((uint8_t*) option
)[0] == 0xff);
136 assert_se(memcmp((uint8_t*) option
+ 1, &iaid
, sizeof(iaid
)) == 0);
137 assert_se(memcmp((uint8_t*) option
+ 5, &duid
, duid_len
) == 0);
148 int dhcp_network_send_raw_socket(int s
, const union sockaddr_union
*link
, const void *packet
, size_t len
) {
150 _cleanup_free_ DHCPPacket
*discover
;
151 uint16_t ip_check
, udp_check
;
156 size
= sizeof(DHCPPacket
);
157 assert_se(len
> size
);
159 discover
= memdup(packet
, len
);
161 assert_se(discover
->ip
.ttl
== IPDEFTTL
);
162 assert_se(discover
->ip
.protocol
== IPPROTO_UDP
);
163 assert_se(discover
->ip
.saddr
== INADDR_ANY
);
164 assert_se(discover
->ip
.daddr
== INADDR_BROADCAST
);
165 assert_se(discover
->udp
.source
== be16toh(DHCP_PORT_CLIENT
));
166 assert_se(discover
->udp
.dest
== be16toh(DHCP_PORT_SERVER
));
168 ip_check
= discover
->ip
.check
;
170 discover
->ip
.ttl
= 0;
171 discover
->ip
.check
= discover
->udp
.len
;
173 udp_check
= ~dhcp_packet_checksum((uint8_t*)&discover
->ip
.ttl
, len
- 8);
174 assert_se(udp_check
== 0xffff);
176 discover
->ip
.ttl
= IPDEFTTL
;
177 discover
->ip
.check
= ip_check
;
179 ip_check
= ~dhcp_packet_checksum((uint8_t*)&discover
->ip
, sizeof(discover
->ip
));
180 assert_se(ip_check
== 0xffff);
182 assert_se(discover
->dhcp
.xid
);
183 assert_se(memcmp(discover
->dhcp
.chaddr
, &mac_addr
, ETH_ALEN
) == 0);
185 size
= len
- sizeof(struct iphdr
) - sizeof(struct udphdr
);
187 assert_se(callback_recv
);
188 callback_recv(size
, &discover
->dhcp
);
193 int dhcp_network_bind_raw_socket(
195 union sockaddr_union
*link
,
197 const uint8_t *addr
, size_t addr_len
,
200 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, test_fd
) < 0)
206 int dhcp_network_bind_udp_socket(be32_t address
, uint16_t port
) {
209 fd
= socket(AF_INET
, SOCK_DGRAM
|SOCK_CLOEXEC
, 0);
216 int dhcp_network_send_udp_socket(int s
, be32_t address
, uint16_t port
, const void *packet
, size_t len
) {
220 static int test_discover_message_verify(size_t size
, struct DHCPMessage
*dhcp
) {
223 res
= dhcp_option_parse(dhcp
, size
, check_options
, NULL
, NULL
);
224 assert_se(res
== DHCP_DISCOVER
);
227 printf(" recv DHCP Discover 0x%08x\n", be32toh(dhcp
->xid
));
232 static void test_discover_message(sd_event
*e
) {
233 sd_dhcp_client
*client
;
237 printf("* %s\n", __FUNCTION__
);
239 r
= sd_dhcp_client_new(&client
);
243 r
= sd_dhcp_client_attach_event(client
, e
, 0);
246 assert_se(sd_dhcp_client_set_index(client
, 42) >= 0);
247 assert_se(sd_dhcp_client_set_mac(client
, mac_addr
, ETH_ALEN
, ARPHRD_ETHER
) >= 0);
249 assert_se(sd_dhcp_client_set_request_option(client
, 248) >= 0);
251 callback_recv
= test_discover_message_verify
;
253 res
= sd_dhcp_client_start(client
);
255 assert_se(res
== 0 || res
== -EINPROGRESS
);
257 sd_event_run(e
, (uint64_t) -1);
259 sd_dhcp_client_stop(client
);
260 sd_dhcp_client_unref(client
);
262 test_fd
[1] = safe_close(test_fd
[1]);
264 callback_recv
= NULL
;
267 static uint8_t test_addr_acq_offer
[] = {
268 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
269 0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
270 0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
271 0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
272 0x6f, 0x95, 0x2f, 0x30, 0x00, 0x00, 0x00, 0x00,
273 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
274 0xc0, 0xa8, 0x02, 0x01, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
301 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x36,
302 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
303 0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff,
304 0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
305 0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
306 0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
307 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
311 static uint8_t test_addr_acq_ack
[] = {
312 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
313 0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
314 0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
315 0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
316 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
317 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
318 0xc0, 0xa8, 0x02, 0x01, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
345 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,
346 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
347 0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff,
348 0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
349 0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
350 0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
351 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
352 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
355 static void test_addr_acq_acquired(sd_dhcp_client
*client
, int event
,
357 sd_event
*e
= userdata
;
358 sd_dhcp_lease
*lease
;
362 assert_se(event
== SD_DHCP_CLIENT_EVENT_IP_ACQUIRE
);
364 assert_se(sd_dhcp_client_get_lease(client
, &lease
) >= 0);
367 assert_se(sd_dhcp_lease_get_address(lease
, &addr
) >= 0);
368 assert_se(memcmp(&addr
.s_addr
, &test_addr_acq_ack
[44],
369 sizeof(addr
.s_addr
)) == 0);
371 assert_se(sd_dhcp_lease_get_netmask(lease
, &addr
) >= 0);
372 assert_se(memcmp(&addr
.s_addr
, &test_addr_acq_ack
[285],
373 sizeof(addr
.s_addr
)) == 0);
375 assert_se(sd_dhcp_lease_get_router(lease
, &addr
) >= 0);
376 assert_se(memcmp(&addr
.s_addr
, &test_addr_acq_ack
[308],
377 sizeof(addr
.s_addr
)) == 0);
380 printf(" DHCP address acquired\n");
385 static int test_addr_acq_recv_request(size_t size
, DHCPMessage
*request
) {
386 uint16_t udp_check
= 0;
387 uint8_t *msg_bytes
= (uint8_t *)request
;
390 res
= dhcp_option_parse(request
, size
, check_options
, NULL
, NULL
);
391 assert_se(res
== DHCP_REQUEST
);
392 assert_se(xid
== request
->xid
);
394 assert_se(msg_bytes
[size
- 1] == SD_DHCP_OPTION_END
);
397 printf(" recv DHCP Request 0x%08x\n", be32toh(xid
));
399 memcpy(&test_addr_acq_ack
[26], &udp_check
, sizeof(udp_check
));
400 memcpy(&test_addr_acq_ack
[32], &xid
, sizeof(xid
));
401 memcpy(&test_addr_acq_ack
[56], &mac_addr
, ETHER_ADDR_LEN
);
403 callback_recv
= NULL
;
405 res
= write(test_fd
[1], test_addr_acq_ack
,
406 sizeof(test_addr_acq_ack
));
407 assert_se(res
== sizeof(test_addr_acq_ack
));
410 printf(" send DHCP Ack\n");
415 static int test_addr_acq_recv_discover(size_t size
, DHCPMessage
*discover
) {
416 uint16_t udp_check
= 0;
417 uint8_t *msg_bytes
= (uint8_t *)discover
;
420 res
= dhcp_option_parse(discover
, size
, check_options
, NULL
, NULL
);
421 assert_se(res
== DHCP_DISCOVER
);
423 assert_se(msg_bytes
[size
- 1] == SD_DHCP_OPTION_END
);
428 printf(" recv DHCP Discover 0x%08x\n", be32toh(xid
));
430 memcpy(&test_addr_acq_offer
[26], &udp_check
, sizeof(udp_check
));
431 memcpy(&test_addr_acq_offer
[32], &xid
, sizeof(xid
));
432 memcpy(&test_addr_acq_offer
[56], &mac_addr
, ETHER_ADDR_LEN
);
434 callback_recv
= test_addr_acq_recv_request
;
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
));
441 printf(" sent DHCP Offer\n");
446 static void test_addr_acq(sd_event
*e
) {
447 usec_t time_now
= now(clock_boottime_or_monotonic());
448 sd_dhcp_client
*client
;
452 printf("* %s\n", __FUNCTION__
);
454 r
= sd_dhcp_client_new(&client
);
458 r
= sd_dhcp_client_attach_event(client
, e
, 0);
461 assert_se(sd_dhcp_client_set_index(client
, 42) >= 0);
462 assert_se(sd_dhcp_client_set_mac(client
, mac_addr
, ETH_ALEN
, ARPHRD_ETHER
) >= 0);
464 assert_se(sd_dhcp_client_set_callback(client
, test_addr_acq_acquired
, e
) >= 0);
466 callback_recv
= test_addr_acq_recv_discover
;
468 assert_se(sd_event_add_time(e
, &test_hangcheck
,
469 clock_boottime_or_monotonic(),
470 time_now
+ 2 * USEC_PER_SEC
, 0,
471 test_dhcp_hangcheck
, NULL
) >= 0);
473 res
= sd_dhcp_client_start(client
);
474 assert_se(res
== 0 || res
== -EINPROGRESS
);
476 assert_se(sd_event_loop(e
) >= 0);
478 test_hangcheck
= sd_event_source_unref(test_hangcheck
);
480 assert_se(sd_dhcp_client_set_callback(client
, NULL
, NULL
) >= 0);
481 assert_se(sd_dhcp_client_stop(client
) >= 0);
482 sd_dhcp_client_unref(client
);
484 test_fd
[1] = safe_close(test_fd
[1]);
486 callback_recv
= NULL
;
490 int main(int argc
, char *argv
[]) {
491 _cleanup_(sd_event_unrefp
) sd_event
*e
;
493 log_set_max_level(LOG_DEBUG
);
494 log_parse_environment();
497 assert_se(sd_event_new(&e
) >= 0);
499 test_request_basic(e
);
502 test_discover_message(e
);
506 /* Make sure the async_close thread has finished.
507 * valgrind would report some of the phread_* structures
508 * as not cleaned up properly. */