1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2013 Intel Corporation. All rights reserved.
6 #include <net/if_arp.h>
8 #include "sd-dhcp-server.h"
11 #include "dhcp-server-internal.h"
13 #include "siphash24.h"
16 static void test_pool(struct in_addr
*address
, unsigned size
, int ret
) {
17 _cleanup_(sd_dhcp_server_unrefp
) sd_dhcp_server
*server
= NULL
;
19 ASSERT_OK(sd_dhcp_server_new(&server
, 1));
22 ASSERT_RETURN_IS_CRITICAL(true, ASSERT_OK_EQ(sd_dhcp_server_configure_pool(server
, address
, 8, 0, size
), ret
));
24 ASSERT_RETURN_IS_CRITICAL(false, ASSERT_ERROR(sd_dhcp_server_configure_pool(server
, address
, 8, 0, size
), -ret
));
27 static int test_basic(bool bind_to_interface
) {
28 _cleanup_(sd_dhcp_server_unrefp
) sd_dhcp_server
*server
= NULL
;
29 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
30 struct in_addr address_lo
= {
31 .s_addr
= htobe32(INADDR_LOOPBACK
),
33 struct in_addr address_any
= {
34 .s_addr
= htobe32(INADDR_ANY
),
38 log_debug("/* %s(bind_to_interface=%s) */", __func__
, yes_no(bind_to_interface
));
40 ASSERT_OK(sd_event_new(&event
));
42 /* attach to loopback interface */
43 ASSERT_OK(sd_dhcp_server_new(&server
, 1));
44 ASSERT_NOT_NULL(server
);
45 server
->bind_to_interface
= bind_to_interface
;
47 ASSERT_OK(sd_dhcp_server_attach_event(server
, event
, 0));
48 ASSERT_RETURN_EXPECTED(ASSERT_ERROR(sd_dhcp_server_attach_event(server
, event
, 0), EBUSY
));
49 ASSERT_TRUE(sd_dhcp_server_get_event(server
) == event
); /* ASSERT_EQ() doesn't work here. */
50 ASSERT_OK(sd_dhcp_server_detach_event(server
));
51 ASSERT_NULL(sd_dhcp_server_get_event(server
));
52 ASSERT_OK(sd_dhcp_server_attach_event(server
, NULL
, 0));
53 ASSERT_RETURN_EXPECTED(ASSERT_ERROR(sd_dhcp_server_attach_event(server
, NULL
, 0), EBUSY
));
55 ASSERT_TRUE(sd_dhcp_server_ref(server
) == server
);
56 ASSERT_NULL(sd_dhcp_server_unref(server
));
58 ASSERT_RETURN_EXPECTED(ASSERT_ERROR(sd_dhcp_server_start(server
), EUNATCH
));
60 ASSERT_RETURN_EXPECTED(ASSERT_ERROR(sd_dhcp_server_configure_pool(server
, &address_any
, 28, 0, 0), EINVAL
));
61 ASSERT_RETURN_EXPECTED(ASSERT_ERROR(sd_dhcp_server_configure_pool(server
, &address_lo
, 38, 0, 0), ERANGE
));
62 ASSERT_OK(sd_dhcp_server_configure_pool(server
, &address_lo
, 8, 0, 0));
63 ASSERT_OK(sd_dhcp_server_configure_pool(server
, &address_lo
, 8, 0, 0));
65 test_pool(&address_any
, 1, -EINVAL
);
66 test_pool(&address_lo
, 1, 0);
68 r
= sd_dhcp_server_start(server
);
69 /* skip test if running in an environment with no full networking support, CONFIG_PACKET not
70 * compiled in kernel, nor af_packet module available. */
71 if (r
== -EPERM
|| r
== -EAFNOSUPPORT
)
75 ASSERT_OK(sd_dhcp_server_start(server
));
76 ASSERT_OK(sd_dhcp_server_stop(server
));
77 ASSERT_OK(sd_dhcp_server_stop(server
));
78 ASSERT_OK(sd_dhcp_server_start(server
));
83 static void test_message_handler(void) {
84 _cleanup_(sd_dhcp_server_unrefp
) sd_dhcp_server
*server
= NULL
;
87 DHCP_MESSAGE_HEADER_DEFINITION
;
93 } _packed_ option_type
;
98 } _packed_ option_requested_ip
;
103 } _packed_ option_server_id
;
108 } _packed_ option_client_id
;
113 } _packed_ option_hostname
;
116 .message
.op
= BOOTREQUEST
,
117 .message
.htype
= ARPHRD_ETHER
,
118 .message
.hlen
= ETHER_ADDR_LEN
,
119 .message
.xid
= htobe32(0x12345678),
120 .message
.chaddr
= { 'A', 'B', 'C', 'D', 'E', 'F' },
121 .option_type
.code
= SD_DHCP_OPTION_MESSAGE_TYPE
,
122 .option_type
.length
= 1,
123 .option_type
.type
= DHCP_DISCOVER
,
124 .option_hostname
.code
= SD_DHCP_OPTION_HOST_NAME
,
125 .option_hostname
.length
= 6,
126 .option_hostname
.hostname
= { 'T', 'E', 'S', 'T', 'H', 'N' },
127 .end
= SD_DHCP_OPTION_END
,
129 struct in_addr address_lo
= {
130 .s_addr
= htobe32(INADDR_LOOPBACK
),
132 struct in_addr static_lease_address
= {
133 .s_addr
= htobe32(INADDR_LOOPBACK
+ 42),
135 static uint8_t static_lease_client_id
[7] = {0x01, 'A', 'B', 'C', 'D', 'E', 'G' };
138 log_debug("/* %s */", __func__
);
140 ASSERT_OK(sd_dhcp_server_new(&server
, 1));
141 ASSERT_OK(sd_dhcp_server_configure_pool(server
, &address_lo
, 8, 0, 0));
142 ASSERT_OK(sd_dhcp_server_set_static_lease(server
, &static_lease_address
, static_lease_client_id
,
143 ELEMENTSOF(static_lease_client_id
)));
144 ASSERT_OK(sd_dhcp_server_attach_event(server
, NULL
, 0));
145 ASSERT_OK(sd_dhcp_server_start(server
));
147 r
= dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
);
149 return (void) log_tests_skipped("Network is not available");
150 ASSERT_OK_EQ(r
, DHCP_OFFER
);
153 /* TODO, shouldn't this fail? */
154 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_OFFER
);
155 test
.end
= SD_DHCP_OPTION_END
;
156 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_OFFER
);
158 test
.option_type
.code
= 0;
159 test
.option_type
.length
= 0;
160 test
.option_type
.type
= 0;
161 ASSERT_ERROR(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), ENOMSG
);
162 test
.option_type
.code
= SD_DHCP_OPTION_MESSAGE_TYPE
;
163 test
.option_type
.length
= 1;
164 test
.option_type
.type
= DHCP_DISCOVER
;
165 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_OFFER
);
168 ASSERT_OK_ZERO(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
));
169 test
.message
.op
= BOOTREQUEST
;
170 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_OFFER
);
172 test
.message
.htype
= 0;
173 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_OFFER
);
174 test
.message
.htype
= ARPHRD_ETHER
;
175 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_OFFER
);
177 test
.message
.hlen
= 0;
178 ASSERT_ERROR(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), EBADMSG
);
179 test
.message
.hlen
= ETHER_ADDR_LEN
;
180 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_OFFER
);
182 test
.option_type
.type
= DHCP_REQUEST
;
183 ASSERT_OK_ZERO(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
));
184 test
.option_requested_ip
.code
= SD_DHCP_OPTION_REQUESTED_IP_ADDRESS
;
185 test
.option_requested_ip
.length
= 4;
186 test
.option_requested_ip
.address
= htobe32(0x12345678);
187 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_NAK
);
188 test
.option_server_id
.code
= SD_DHCP_OPTION_SERVER_IDENTIFIER
;
189 test
.option_server_id
.length
= 4;
190 test
.option_server_id
.address
= htobe32(INADDR_LOOPBACK
);
191 test
.option_requested_ip
.address
= htobe32(INADDR_LOOPBACK
+ 3);
192 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_ACK
);
194 test
.option_server_id
.address
= htobe32(0x12345678);
195 test
.option_requested_ip
.address
= htobe32(INADDR_LOOPBACK
+ 3);
196 ASSERT_OK_ZERO(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
));
197 test
.option_server_id
.address
= htobe32(INADDR_LOOPBACK
);
198 test
.option_requested_ip
.address
= htobe32(INADDR_LOOPBACK
+ 4);
199 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_ACK
);
200 test
.option_requested_ip
.address
= htobe32(INADDR_LOOPBACK
+ 3);
201 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_ACK
);
203 test
.option_client_id
.code
= SD_DHCP_OPTION_CLIENT_IDENTIFIER
;
204 test
.option_client_id
.length
= 7;
205 test
.option_client_id
.id
[0] = 0x01;
206 test
.option_client_id
.id
[1] = 'A';
207 test
.option_client_id
.id
[2] = 'B';
208 test
.option_client_id
.id
[3] = 'C';
209 test
.option_client_id
.id
[4] = 'D';
210 test
.option_client_id
.id
[5] = 'E';
211 test
.option_client_id
.id
[6] = 'F';
212 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_ACK
);
214 test
.option_requested_ip
.address
= htobe32(INADDR_LOOPBACK
+ 30);
215 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_ACK
);
217 /* request address reserved for static lease (unmatching client ID) */
218 test
.option_client_id
.id
[6] = 'H';
219 test
.option_requested_ip
.address
= htobe32(INADDR_LOOPBACK
+ 42);
220 ASSERT_OK_ZERO(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
));
222 /* request unmatching address */
223 test
.option_client_id
.id
[6] = 'G';
224 test
.option_requested_ip
.address
= htobe32(INADDR_LOOPBACK
+ 41);
225 ASSERT_OK_ZERO(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
));
227 /* request matching address */
228 test
.option_client_id
.id
[6] = 'G';
229 test
.option_requested_ip
.address
= htobe32(INADDR_LOOPBACK
+ 42);
230 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_ACK
);
233 test
.option_client_id
.id
[6] = 'G';
234 test
.option_requested_ip
.address
= htobe32(INADDR_LOOPBACK
+ 42);
235 ASSERT_OK_EQ(dhcp_server_handle_message(server
, (DHCPMessage
*)&test
, sizeof(test
), NULL
), DHCP_ACK
);
238 static uint64_t client_id_hash_helper(sd_dhcp_client_id
*id
, uint8_t key
[HASH_KEY_SIZE
]) {
239 struct siphash state
;
241 siphash24_init(&state
, key
);
242 client_id_hash_func(id
, &state
);
244 return htole64(siphash24_finalize(&state
));
247 static void test_client_id_hash(void) {
248 sd_dhcp_client_id a
= {
253 uint8_t hash_key
[HASH_KEY_SIZE
] = {
254 '0', '1', '2', '3', '4', '5', '6', '7',
255 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
258 log_debug("/* %s */", __func__
);
260 memcpy(a
.raw
, "abcd", 4);
261 memcpy(b
.raw
, "abcd", 4);
263 ASSERT_EQ(client_id_compare_func(&a
, &b
), 0);
264 ASSERT_EQ(client_id_hash_helper(&a
, hash_key
), client_id_hash_helper(&b
, hash_key
));
266 ASSERT_NE(client_id_compare_func(&a
, &b
), 0);
268 ASSERT_EQ(client_id_compare_func(&a
, &b
), 0);
269 ASSERT_EQ(client_id_hash_helper(&a
, hash_key
), client_id_hash_helper(&b
, hash_key
));
272 ASSERT_NE(client_id_compare_func(&a
, &b
), 0);
274 ASSERT_EQ(client_id_compare_func(&a
, &b
), 0);
275 ASSERT_EQ(client_id_hash_helper(&a
, hash_key
), client_id_hash_helper(&b
, hash_key
));
277 memcpy(b
.raw
, "abce", 4);
278 ASSERT_NE(client_id_compare_func(&a
, &b
), 0);
281 static void test_static_lease(void) {
282 _cleanup_(sd_dhcp_server_unrefp
) sd_dhcp_server
*server
= NULL
;
284 log_debug("/* %s */", __func__
);
286 ASSERT_OK(sd_dhcp_server_new(&server
, 1));
288 ASSERT_OK(sd_dhcp_server_set_static_lease(server
, &(struct in_addr
) { .s_addr
= 0x01020304 },
289 (uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t)));
290 /* Duplicated entry. */
291 ASSERT_ERROR(sd_dhcp_server_set_static_lease(server
, &(struct in_addr
) { .s_addr
= 0x01020304 },
292 (uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t)), EEXIST
);
293 /* Address is conflicted. */
294 ASSERT_ERROR(sd_dhcp_server_set_static_lease(server
, &(struct in_addr
) { .s_addr
= 0x01020304 },
295 (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)), EEXIST
);
296 /* Client ID is conflicted. */
297 ASSERT_ERROR(sd_dhcp_server_set_static_lease(server
, &(struct in_addr
) { .s_addr
= 0x01020305 },
298 (uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t)), EEXIST
);
300 ASSERT_OK(sd_dhcp_server_set_static_lease(server
, &(struct in_addr
) { .s_addr
= 0x01020305 },
301 (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)));
302 /* Remove the previous entry. */
303 ASSERT_OK(sd_dhcp_server_set_static_lease(server
, &(struct in_addr
) { .s_addr
= 0x00000000 },
304 (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)));
305 /* Then, set a different address. */
306 ASSERT_OK(sd_dhcp_server_set_static_lease(server
, &(struct in_addr
) { .s_addr
= 0x01020306 },
307 (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)));
309 ASSERT_OK(sd_dhcp_server_set_static_lease(server
, &(struct in_addr
) { .s_addr
= 0x00000000 },
310 (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)));
311 /* Try to remove non-existent entry. */
312 ASSERT_OK(sd_dhcp_server_set_static_lease(server
, &(struct in_addr
) { .s_addr
= 0x00000000 },
313 (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)));
314 /* Try to remove non-existent entry. */
315 ASSERT_OK(sd_dhcp_server_set_static_lease(server
, &(struct in_addr
) { .s_addr
= 0x00000000 },
316 (uint8_t*) &(uint32_t) { 0x01020306 }, sizeof(uint32_t)));
319 int main(int argc
, char *argv
[]) {
322 test_setup_logging(LOG_DEBUG
);
324 test_client_id_hash();
327 r
= test_basic(true);
329 return log_tests_skipped_errno(r
, "cannot start dhcp server(bound to interface)");
331 r
= test_basic(false);
333 return log_tests_skipped_errno(r
, "cannot start dhcp server(non-bound to interface)");
335 test_message_handler();