]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/libsystemd-network/test-dhcp-server.c
seccomp-util: allowlist open_tree() as part of @file-system
[thirdparty/systemd.git] / src / libsystemd-network / test-dhcp-server.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2/***
3 Copyright © 2013 Intel Corporation. All rights reserved.
4***/
5
6#include <net/if_arp.h>
7
8#include "sd-dhcp-server.h"
9#include "sd-event.h"
10
11#include "dhcp-server-internal.h"
12#include "hashmap.h"
13#include "siphash24.h"
14#include "tests.h"
15
16static void test_pool(struct in_addr *address, unsigned size, int ret) {
17 _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
18
19 ASSERT_OK(sd_dhcp_server_new(&server, 1));
20
21 if (ret >= 0)
22 ASSERT_RETURN_IS_CRITICAL(true, ASSERT_OK_EQ(sd_dhcp_server_configure_pool(server, address, 8, 0, size), ret));
23 else
24 ASSERT_RETURN_IS_CRITICAL(false, ASSERT_ERROR(sd_dhcp_server_configure_pool(server, address, 8, 0, size), -ret));
25}
26
27static 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),
32 };
33 struct in_addr address_any = {
34 .s_addr = htobe32(INADDR_ANY),
35 };
36 int r;
37
38 log_debug("/* %s(bind_to_interface=%s) */", __func__, yes_no(bind_to_interface));
39
40 ASSERT_OK(sd_event_new(&event));
41
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;
46
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));
54
55 ASSERT_TRUE(sd_dhcp_server_ref(server) == server);
56 ASSERT_NULL(sd_dhcp_server_unref(server));
57
58 ASSERT_RETURN_EXPECTED(ASSERT_ERROR(sd_dhcp_server_start(server), EUNATCH));
59
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));
64
65 test_pool(&address_any, 1, -EINVAL);
66 test_pool(&address_lo, 1, 0);
67
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)
72 return r;
73 ASSERT_OK(r);
74
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));
79
80 return 0;
81}
82
83static void test_message_handler(void) {
84 _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
85 struct {
86 struct {
87 DHCP_MESSAGE_HEADER_DEFINITION;
88 } _packed_ message;
89 struct {
90 uint8_t code;
91 uint8_t length;
92 uint8_t type;
93 } _packed_ option_type;
94 struct {
95 uint8_t code;
96 uint8_t length;
97 be32_t address;
98 } _packed_ option_requested_ip;
99 struct {
100 uint8_t code;
101 uint8_t length;
102 be32_t address;
103 } _packed_ option_server_id;
104 struct {
105 uint8_t code;
106 uint8_t length;
107 uint8_t id[7];
108 } _packed_ option_client_id;
109 struct {
110 uint8_t code;
111 uint8_t length;
112 uint8_t hostname[6];
113 } _packed_ option_hostname;
114 uint8_t end;
115 } _packed_ test = {
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,
128 };
129 struct in_addr address_lo = {
130 .s_addr = htobe32(INADDR_LOOPBACK),
131 };
132 struct in_addr static_lease_address = {
133 .s_addr = htobe32(INADDR_LOOPBACK + 42),
134 };
135 static uint8_t static_lease_client_id[7] = {0x01, 'A', 'B', 'C', 'D', 'E', 'G' };
136 int r;
137
138 log_debug("/* %s */", __func__);
139
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));
146
147 r = dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL);
148 if (r == -ENETDOWN)
149 return (void) log_tests_skipped("Network is not available");
150 ASSERT_OK_EQ(r, DHCP_OFFER);
151
152 test.end = 0;
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);
157
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);
166
167 test.message.op = 0;
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);
171
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);
176
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);
181
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);
193
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);
202
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);
213
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);
216
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));
221
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));
226
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);
231
232 /* try again */
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);
236}
237
238static uint64_t client_id_hash_helper(sd_dhcp_client_id *id, uint8_t key[HASH_KEY_SIZE]) {
239 struct siphash state;
240
241 siphash24_init(&state, key);
242 client_id_hash_func(id, &state);
243
244 return htole64(siphash24_finalize(&state));
245}
246
247static void test_client_id_hash(void) {
248 sd_dhcp_client_id a = {
249 .size = 4,
250 }, b = {
251 .size = 4,
252 };
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',
256 };
257
258 log_debug("/* %s */", __func__);
259
260 memcpy(a.raw, "abcd", 4);
261 memcpy(b.raw, "abcd", 4);
262
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));
265 a.size = 3;
266 ASSERT_NE(client_id_compare_func(&a, &b), 0);
267 a.size = 4;
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));
270
271 b.size = 3;
272 ASSERT_NE(client_id_compare_func(&a, &b), 0);
273 b.size = 4;
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));
276
277 memcpy(b.raw, "abce", 4);
278 ASSERT_NE(client_id_compare_func(&a, &b), 0);
279}
280
281static void test_static_lease(void) {
282 _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
283
284 log_debug("/* %s */", __func__);
285
286 ASSERT_OK(sd_dhcp_server_new(&server, 1));
287
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);
299
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)));
308 /* Remove again. */
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)));
317}
318
319int main(int argc, char *argv[]) {
320 int r;
321
322 test_setup_logging(LOG_DEBUG);
323
324 test_client_id_hash();
325 test_static_lease();
326
327 r = test_basic(true);
328 if (r < 0)
329 return log_tests_skipped_errno(r, "cannot start dhcp server(bound to interface)");
330
331 r = test_basic(false);
332 if (r < 0)
333 return log_tests_skipped_errno(r, "cannot start dhcp server(non-bound to interface)");
334
335 test_message_handler();
336
337 return 0;
338}