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>
29 #include "event-util.h"
31 #include "dhcp-identifier.h"
32 #include "dhcp-protocol.h"
33 #include "dhcp-internal.h"
34 #include "sd-dhcp-client.h"
36 static uint8_t mac_addr
[] = {'A', 'B', 'C', '1', '2', '3'};
38 typedef int (*test_callback_recv_t
)(size_t size
, DHCPMessage
*dhcp
);
40 static bool verbose
= true;
41 static int test_fd
[2];
42 static test_callback_recv_t callback_recv
;
44 static sd_event_source
*test_hangcheck
;
46 static int test_dhcp_hangcheck(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
47 assert_not_reached("Test case should have completed in 2 seconds");
52 static void test_request_basic(sd_event
*e
) {
55 sd_dhcp_client
*client
;
58 printf("* %s\n", __FUNCTION__
);
60 r
= sd_dhcp_client_new(&client
);
65 r
= sd_dhcp_client_attach_event(client
, e
, 0);
68 assert_se(sd_dhcp_client_set_request_option(NULL
, 0) == -EINVAL
);
69 assert_se(sd_dhcp_client_set_request_address(NULL
, NULL
) == -EINVAL
);
70 assert_se(sd_dhcp_client_set_index(NULL
, 0) == -EINVAL
);
72 assert_se(sd_dhcp_client_set_index(client
, 15) == 0);
73 assert_se(sd_dhcp_client_set_index(client
, -42) == -EINVAL
);
74 assert_se(sd_dhcp_client_set_index(client
, -1) == -EINVAL
);
75 assert_se(sd_dhcp_client_set_index(client
, 0) == -EINVAL
);
76 assert_se(sd_dhcp_client_set_index(client
, 1) == 0);
78 assert_se(sd_dhcp_client_set_request_option(client
,
79 DHCP_OPTION_SUBNET_MASK
) == -EEXIST
);
80 assert_se(sd_dhcp_client_set_request_option(client
,
81 DHCP_OPTION_ROUTER
) == -EEXIST
);
82 assert_se(sd_dhcp_client_set_request_option(client
,
83 DHCP_OPTION_HOST_NAME
) == -EEXIST
);
84 assert_se(sd_dhcp_client_set_request_option(client
,
85 DHCP_OPTION_DOMAIN_NAME
) == -EEXIST
);
86 assert_se(sd_dhcp_client_set_request_option(client
,
87 DHCP_OPTION_DOMAIN_NAME_SERVER
) == -EEXIST
);
89 assert_se(sd_dhcp_client_set_request_option(client
,
90 DHCP_OPTION_PAD
) == -EINVAL
);
91 assert_se(sd_dhcp_client_set_request_option(client
,
92 DHCP_OPTION_END
) == -EINVAL
);
93 assert_se(sd_dhcp_client_set_request_option(client
,
94 DHCP_OPTION_MESSAGE_TYPE
) == -EINVAL
);
95 assert_se(sd_dhcp_client_set_request_option(client
,
96 DHCP_OPTION_OVERLOAD
) == -EINVAL
);
97 assert_se(sd_dhcp_client_set_request_option(client
,
98 DHCP_OPTION_PARAMETER_REQUEST_LIST
)
101 assert_se(sd_dhcp_client_set_request_option(client
, 33) == 0);
102 assert_se(sd_dhcp_client_set_request_option(client
, 33) == -EEXIST
);
103 assert_se(sd_dhcp_client_set_request_option(client
, 44) == 0);
104 assert_se(sd_dhcp_client_set_request_option(client
, 33) == -EEXIST
);
106 sd_dhcp_client_unref(client
);
109 static void test_checksum(void) {
111 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
112 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 0xff, 0xff, 0xff, 0xff
117 printf("* %s\n", __FUNCTION__
);
119 assert_se(dhcp_packet_checksum((uint8_t*)&buf
, 20) == be16toh(0x78ae));
122 static int check_options(uint8_t code
, uint8_t len
, const void *option
, void *userdata
) {
124 case DHCP_OPTION_CLIENT_IDENTIFIER
:
130 assert_se(dhcp_identifier_set_duid_en(&duid
, &duid_len
) >= 0);
131 assert_se(dhcp_identifier_set_iaid(42, mac_addr
, ETH_ALEN
, &iaid
) >= 0);
133 assert_se(len
== sizeof(uint8_t) + sizeof(uint32_t) + duid_len
);
134 assert_se(len
== 19);
135 assert_se(((uint8_t*) option
)[0] == 0xff);
137 assert_se(memcmp((uint8_t*) option
+ 1, &iaid
, sizeof(iaid
)) == 0);
138 assert_se(memcmp((uint8_t*) option
+ 5, &duid
, duid_len
) == 0);
149 int dhcp_network_send_raw_socket(int s
, const union sockaddr_union
*link
, const void *packet
, size_t len
) {
151 _cleanup_free_ DHCPPacket
*discover
;
152 uint16_t ip_check
, udp_check
;
157 size
= sizeof(DHCPPacket
);
158 assert_se(len
> size
);
160 discover
= memdup(packet
, len
);
162 assert_se(discover
->ip
.ttl
== IPDEFTTL
);
163 assert_se(discover
->ip
.protocol
== IPPROTO_UDP
);
164 assert_se(discover
->ip
.saddr
== INADDR_ANY
);
165 assert_se(discover
->ip
.daddr
== INADDR_BROADCAST
);
166 assert_se(discover
->udp
.source
== be16toh(DHCP_PORT_CLIENT
));
167 assert_se(discover
->udp
.dest
== be16toh(DHCP_PORT_SERVER
));
169 ip_check
= discover
->ip
.check
;
171 discover
->ip
.ttl
= 0;
172 discover
->ip
.check
= discover
->udp
.len
;
174 udp_check
= ~dhcp_packet_checksum((uint8_t*)&discover
->ip
.ttl
, len
- 8);
175 assert_se(udp_check
== 0xffff);
177 discover
->ip
.ttl
= IPDEFTTL
;
178 discover
->ip
.check
= ip_check
;
180 ip_check
= ~dhcp_packet_checksum((uint8_t*)&discover
->ip
, sizeof(discover
->ip
));
181 assert_se(ip_check
== 0xffff);
183 assert_se(discover
->dhcp
.xid
);
184 assert_se(memcmp(discover
->dhcp
.chaddr
, &mac_addr
, ETH_ALEN
) == 0);
186 size
= len
- sizeof(struct iphdr
) - sizeof(struct udphdr
);
188 assert_se(callback_recv
);
189 callback_recv(size
, &discover
->dhcp
);
194 int dhcp_network_bind_raw_socket(
196 union sockaddr_union
*link
,
198 const uint8_t *addr
, size_t addr_len
,
201 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, test_fd
) < 0)
207 int dhcp_network_bind_udp_socket(be32_t address
, uint16_t port
) {
210 fd
= socket(AF_INET
, SOCK_DGRAM
|SOCK_CLOEXEC
, 0);
217 int dhcp_network_send_udp_socket(int s
, be32_t address
, uint16_t port
, const void *packet
, size_t len
) {
221 static int test_discover_message_verify(size_t size
, struct DHCPMessage
*dhcp
) {
224 res
= dhcp_option_parse(dhcp
, size
, check_options
, NULL
);
225 assert_se(res
== DHCP_DISCOVER
);
228 printf(" recv DHCP Discover 0x%08x\n", be32toh(dhcp
->xid
));
233 static void test_discover_message(sd_event
*e
) {
234 sd_dhcp_client
*client
;
238 printf("* %s\n", __FUNCTION__
);
240 r
= sd_dhcp_client_new(&client
);
244 r
= sd_dhcp_client_attach_event(client
, e
, 0);
247 assert_se(sd_dhcp_client_set_index(client
, 42) >= 0);
248 assert_se(sd_dhcp_client_set_mac(client
, mac_addr
, ETH_ALEN
, ARPHRD_ETHER
) >= 0);
250 assert_se(sd_dhcp_client_set_request_option(client
, 248) >= 0);
252 callback_recv
= test_discover_message_verify
;
254 res
= sd_dhcp_client_start(client
);
256 assert_se(res
== 0 || res
== -EINPROGRESS
);
258 sd_event_run(e
, (uint64_t) -1);
260 sd_dhcp_client_stop(client
);
261 sd_dhcp_client_unref(client
);
263 test_fd
[1] = safe_close(test_fd
[1]);
265 callback_recv
= NULL
;
268 static uint8_t test_addr_acq_offer
[] = {
269 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
270 0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
271 0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
272 0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
273 0x6f, 0x95, 0x2f, 0x30, 0x00, 0x00, 0x00, 0x00,
274 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
275 0xc0, 0xa8, 0x02, 0x01, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
302 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x36,
303 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
304 0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff,
305 0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
306 0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
307 0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
308 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
312 static uint8_t test_addr_acq_ack
[] = {
313 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
314 0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
315 0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
316 0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
317 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
318 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
319 0xc0, 0xa8, 0x02, 0x01, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,
347 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
348 0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff,
349 0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
350 0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
351 0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
352 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
353 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
356 static void test_addr_acq_acquired(sd_dhcp_client
*client
, int event
,
358 sd_event
*e
= userdata
;
359 sd_dhcp_lease
*lease
;
363 assert_se(event
== DHCP_EVENT_IP_ACQUIRE
);
365 assert_se(sd_dhcp_client_get_lease(client
, &lease
) >= 0);
368 assert_se(sd_dhcp_lease_get_address(lease
, &addr
) >= 0);
369 assert_se(memcmp(&addr
.s_addr
, &test_addr_acq_ack
[44],
370 sizeof(addr
.s_addr
)) == 0);
372 assert_se(sd_dhcp_lease_get_netmask(lease
, &addr
) >= 0);
373 assert_se(memcmp(&addr
.s_addr
, &test_addr_acq_ack
[285],
374 sizeof(addr
.s_addr
)) == 0);
376 assert_se(sd_dhcp_lease_get_router(lease
, &addr
) >= 0);
377 assert_se(memcmp(&addr
.s_addr
, &test_addr_acq_ack
[308],
378 sizeof(addr
.s_addr
)) == 0);
381 printf(" DHCP address acquired\n");
386 static int test_addr_acq_recv_request(size_t size
, DHCPMessage
*request
) {
387 uint16_t udp_check
= 0;
388 uint8_t *msg_bytes
= (uint8_t *)request
;
391 res
= dhcp_option_parse(request
, size
, check_options
, NULL
);
392 assert_se(res
== DHCP_REQUEST
);
393 assert_se(xid
== request
->xid
);
395 assert_se(msg_bytes
[size
- 1] == DHCP_OPTION_END
);
398 printf(" recv DHCP Request 0x%08x\n", be32toh(xid
));
400 memcpy(&test_addr_acq_ack
[26], &udp_check
, sizeof(udp_check
));
401 memcpy(&test_addr_acq_ack
[32], &xid
, sizeof(xid
));
402 memcpy(&test_addr_acq_ack
[56], &mac_addr
, ETHER_ADDR_LEN
);
404 callback_recv
= NULL
;
406 res
= write(test_fd
[1], test_addr_acq_ack
,
407 sizeof(test_addr_acq_ack
));
408 assert_se(res
== sizeof(test_addr_acq_ack
));
411 printf(" send DHCP Ack\n");
416 static int test_addr_acq_recv_discover(size_t size
, DHCPMessage
*discover
) {
417 uint16_t udp_check
= 0;
418 uint8_t *msg_bytes
= (uint8_t *)discover
;
421 res
= dhcp_option_parse(discover
, size
, check_options
, NULL
);
422 assert_se(res
== DHCP_DISCOVER
);
424 assert_se(msg_bytes
[size
- 1] == DHCP_OPTION_END
);
429 printf(" recv DHCP Discover 0x%08x\n", be32toh(xid
));
431 memcpy(&test_addr_acq_offer
[26], &udp_check
, sizeof(udp_check
));
432 memcpy(&test_addr_acq_offer
[32], &xid
, sizeof(xid
));
433 memcpy(&test_addr_acq_offer
[56], &mac_addr
, ETHER_ADDR_LEN
);
435 callback_recv
= test_addr_acq_recv_request
;
437 res
= write(test_fd
[1], test_addr_acq_offer
,
438 sizeof(test_addr_acq_offer
));
439 assert_se(res
== sizeof(test_addr_acq_offer
));
442 printf(" sent DHCP Offer\n");
447 static void test_addr_acq(sd_event
*e
) {
448 usec_t time_now
= now(clock_boottime_or_monotonic());
449 sd_dhcp_client
*client
;
453 printf("* %s\n", __FUNCTION__
);
455 r
= sd_dhcp_client_new(&client
);
459 r
= sd_dhcp_client_attach_event(client
, e
, 0);
462 assert_se(sd_dhcp_client_set_index(client
, 42) >= 0);
463 assert_se(sd_dhcp_client_set_mac(client
, mac_addr
, ETH_ALEN
, ARPHRD_ETHER
) >= 0);
465 assert_se(sd_dhcp_client_set_callback(client
, test_addr_acq_acquired
, e
) >= 0);
467 callback_recv
= test_addr_acq_recv_discover
;
469 assert_se(sd_event_add_time(e
, &test_hangcheck
,
470 clock_boottime_or_monotonic(),
471 time_now
+ 2 * USEC_PER_SEC
, 0,
472 test_dhcp_hangcheck
, NULL
) >= 0);
474 res
= sd_dhcp_client_start(client
);
475 assert_se(res
== 0 || res
== -EINPROGRESS
);
477 assert_se(sd_event_loop(e
) >= 0);
479 test_hangcheck
= sd_event_source_unref(test_hangcheck
);
481 assert_se(sd_dhcp_client_set_callback(client
, NULL
, NULL
) >= 0);
482 assert_se(sd_dhcp_client_stop(client
) >= 0);
483 sd_dhcp_client_unref(client
);
485 test_fd
[1] = safe_close(test_fd
[1]);
487 callback_recv
= NULL
;
491 int main(int argc
, char *argv
[]) {
492 _cleanup_event_unref_ sd_event
*e
;
494 log_set_max_level(LOG_DEBUG
);
495 log_parse_environment();
498 assert_se(sd_event_new(&e
) >= 0);
500 test_request_basic(e
);
503 test_discover_message(e
);
507 /* Make sure the async_close thread has finished.
508 * valgrind would report some of the phread_* structures
509 * as not cleaned up properly. */