]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/test-dhcp-server.c
presets: Disable by default for initrd presets
[thirdparty/systemd.git] / src / libsystemd-network / test-dhcp-server.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
b44cd882 2/***
810adae9 3 Copyright © 2013 Intel Corporation. All rights reserved.
b44cd882
TG
4***/
5
ddb82ec2 6#include <net/if_arp.h>
b44cd882 7
07630cea 8#include "sd-dhcp-server.h"
b44cd882 9#include "sd-event.h"
b44cd882 10
b44cd882 11#include "dhcp-server-internal.h"
5cdf13c7
DDM
12#include "hashmap.h"
13#include "siphash24.h"
317bb217 14#include "tests.h"
b44cd882 15
99634696 16static void test_pool(struct in_addr *address, unsigned size, int ret) {
4afd3348 17 _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
99634696 18
73a1a6f4 19 ASSERT_OK(sd_dhcp_server_new(&server, 1));
99634696 20
73a1a6f4
DDM
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));
99634696
TG
25}
26
4e2319af 27static int test_basic(bool bind_to_interface) {
4afd3348 28 _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
4e2319af 29 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
20af7091 30 struct in_addr address_lo = {
d7a0f1f4 31 .s_addr = htobe32(INADDR_LOOPBACK),
20af7091
TG
32 };
33 struct in_addr address_any = {
d7a0f1f4 34 .s_addr = htobe32(INADDR_ANY),
20af7091 35 };
6f08fb7b 36 int r;
b44cd882 37
99e65b7d
YW
38 log_debug("/* %s(bind_to_interface=%s) */", __func__, yes_no(bind_to_interface));
39
73a1a6f4 40 ASSERT_OK(sd_event_new(&event));
4e2319af 41
3a864fe4 42 /* attach to loopback interface */
73a1a6f4
DDM
43 ASSERT_OK(sd_dhcp_server_new(&server, 1));
44 ASSERT_NOT_NULL(server);
21b6b87e 45 server->bind_to_interface = bind_to_interface;
b44cd882 46
73a1a6f4
DDM
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));
b44cd882 54
73a1a6f4
DDM
55 ASSERT_TRUE(sd_dhcp_server_ref(server) == server);
56 ASSERT_NULL(sd_dhcp_server_unref(server));
ff734080 57
73a1a6f4 58 ASSERT_RETURN_EXPECTED(ASSERT_ERROR(sd_dhcp_server_start(server), EUNATCH));
20af7091 59
73a1a6f4
DDM
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));
99634696
TG
64
65 test_pool(&address_any, 1, -EINVAL);
66 test_pool(&address_lo, 1, 0);
2dead812 67
6f08fb7b 68 r = sd_dhcp_server_start(server);
19614a08
RK
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)
7e0a8bf1 72 return r;
73a1a6f4 73 ASSERT_OK(r);
6f08fb7b 74
73a1a6f4
DDM
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));
6f08fb7b
TG
79
80 return 0;
b44cd882
TG
81}
82
be077570 83static void test_message_handler(void) {
4afd3348 84 _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
be077570 85 struct {
5b160db5
YW
86 struct {
87 DHCP_MESSAGE_HEADER_DEFINITION;
88 } _packed_ message;
be077570
TG
89 struct {
90 uint8_t code;
91 uint8_t length;
92 uint8_t type;
93 } _packed_ option_type;
2dead812
TG
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;
87322b3a
TG
104 struct {
105 uint8_t code;
106 uint8_t length;
107 uint8_t id[7];
108 } _packed_ option_client_id;
930133d5
RH
109 struct {
110 uint8_t code;
111 uint8_t length;
112 uint8_t hostname[6];
113 } _packed_ option_hostname;
be077570
TG
114 uint8_t end;
115 } _packed_ test = {
116 .message.op = BOOTREQUEST,
117 .message.htype = ARPHRD_ETHER,
118 .message.hlen = ETHER_ADDR_LEN,
2dead812
TG
119 .message.xid = htobe32(0x12345678),
120 .message.chaddr = { 'A', 'B', 'C', 'D', 'E', 'F' },
22805d92 121 .option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE,
be077570
TG
122 .option_type.length = 1,
123 .option_type.type = DHCP_DISCOVER,
930133d5
RH
124 .option_hostname.code = SD_DHCP_OPTION_HOST_NAME,
125 .option_hostname.length = 6,
126 .option_hostname.hostname = { 'T', 'E', 'S', 'T', 'H', 'N' },
22805d92 127 .end = SD_DHCP_OPTION_END,
be077570 128 };
20af7091 129 struct in_addr address_lo = {
d7a0f1f4 130 .s_addr = htobe32(INADDR_LOOPBACK),
20af7091 131 };
6796c5a9
YW
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' };
4cf7a676 136 int r;
be077570 137
99e65b7d
YW
138 log_debug("/* %s */", __func__);
139
73a1a6f4
DDM
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));
be077570 146
4cf7a676
DDM
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");
73a1a6f4 150 ASSERT_OK_EQ(r, DHCP_OFFER);
be077570
TG
151
152 test.end = 0;
153 /* TODO, shouldn't this fail? */
73a1a6f4 154 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_OFFER);
22805d92 155 test.end = SD_DHCP_OPTION_END;
73a1a6f4 156 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_OFFER);
be077570
TG
157
158 test.option_type.code = 0;
159 test.option_type.length = 0;
160 test.option_type.type = 0;
73a1a6f4 161 ASSERT_ERROR(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), ENOMSG);
22805d92 162 test.option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE;
be077570
TG
163 test.option_type.length = 1;
164 test.option_type.type = DHCP_DISCOVER;
73a1a6f4 165 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_OFFER);
be077570
TG
166
167 test.message.op = 0;
73a1a6f4 168 ASSERT_OK_ZERO(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL));
be077570 169 test.message.op = BOOTREQUEST;
73a1a6f4 170 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_OFFER);
be077570
TG
171
172 test.message.htype = 0;
73a1a6f4 173 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_OFFER);
be077570 174 test.message.htype = ARPHRD_ETHER;
73a1a6f4 175 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_OFFER);
be077570
TG
176
177 test.message.hlen = 0;
73a1a6f4 178 ASSERT_ERROR(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), EBADMSG);
be077570 179 test.message.hlen = ETHER_ADDR_LEN;
73a1a6f4 180 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_OFFER);
2dead812
TG
181
182 test.option_type.type = DHCP_REQUEST;
73a1a6f4 183 ASSERT_OK_ZERO(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL));
22805d92 184 test.option_requested_ip.code = SD_DHCP_OPTION_REQUESTED_IP_ADDRESS;
2dead812
TG
185 test.option_requested_ip.length = 4;
186 test.option_requested_ip.address = htobe32(0x12345678);
73a1a6f4 187 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_NAK);
22805d92 188 test.option_server_id.code = SD_DHCP_OPTION_SERVER_IDENTIFIER;
2dead812
TG
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);
73a1a6f4 192 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_ACK);
87322b3a 193
2dead812
TG
194 test.option_server_id.address = htobe32(0x12345678);
195 test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
73a1a6f4 196 ASSERT_OK_ZERO(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL));
bd57b450 197 test.option_server_id.address = htobe32(INADDR_LOOPBACK);
87322b3a 198 test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 4);
73a1a6f4 199 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_ACK);
87322b3a 200 test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
73a1a6f4 201 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_ACK);
87322b3a 202
22805d92 203 test.option_client_id.code = SD_DHCP_OPTION_CLIENT_IDENTIFIER;
87322b3a
TG
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';
73a1a6f4 212 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_ACK);
87322b3a 213
bd57b450 214 test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30);
73a1a6f4 215 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_ACK);
6796c5a9
YW
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);
73a1a6f4 220 ASSERT_OK_ZERO(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL));
6796c5a9
YW
221
222 /* request unmatching address */
223 test.option_client_id.id[6] = 'G';
224 test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 41);
73a1a6f4 225 ASSERT_OK_ZERO(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL));
6796c5a9
YW
226
227 /* request matching address */
228 test.option_client_id.id[6] = 'G';
229 test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42);
73a1a6f4 230 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_ACK);
6796c5a9
YW
231
232 /* try again */
233 test.option_client_id.id[6] = 'G';
234 test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42);
73a1a6f4 235 ASSERT_OK_EQ(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL), DHCP_ACK);
be077570
TG
236}
237
95c48613 238static uint64_t client_id_hash_helper(sd_dhcp_client_id *id, uint8_t key[HASH_KEY_SIZE]) {
b826ab58
TG
239 struct siphash state;
240
0cb3c286 241 siphash24_init(&state, key);
b826ab58 242 client_id_hash_func(id, &state);
0cb3c286 243
933f9cae 244 return htole64(siphash24_finalize(&state));
b826ab58
TG
245}
246
87322b3a 247static void test_client_id_hash(void) {
95c48613
YW
248 sd_dhcp_client_id a = {
249 .size = 4,
87322b3a 250 }, b = {
95c48613 251 .size = 4,
87322b3a
TG
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
99e65b7d
YW
258 log_debug("/* %s */", __func__);
259
95c48613
YW
260 memcpy(a.raw, "abcd", 4);
261 memcpy(b.raw, "abcd", 4);
87322b3a 262
73a1a6f4
DDM
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));
95c48613 265 a.size = 3;
73a1a6f4 266 ASSERT_NE(client_id_compare_func(&a, &b), 0);
95c48613 267 a.size = 4;
73a1a6f4
DDM
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));
87322b3a 270
95c48613 271 b.size = 3;
73a1a6f4 272 ASSERT_NE(client_id_compare_func(&a, &b), 0);
95c48613 273 b.size = 4;
73a1a6f4
DDM
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));
87322b3a 276
95c48613 277 memcpy(b.raw, "abce", 4);
73a1a6f4 278 ASSERT_NE(client_id_compare_func(&a, &b), 0);
87322b3a
TG
279}
280
7b5445e7
YW
281static void test_static_lease(void) {
282 _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
283
284 log_debug("/* %s */", __func__);
285
73a1a6f4 286 ASSERT_OK(sd_dhcp_server_new(&server, 1));
7b5445e7 287
73a1a6f4
DDM
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)));
7b5445e7 290 /* Duplicated entry. */
73a1a6f4
DDM
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);
7b5445e7 293 /* Address is conflicted. */
73a1a6f4
DDM
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);
7b5445e7 296 /* Client ID is conflicted. */
73a1a6f4
DDM
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);
7b5445e7 299
73a1a6f4
DDM
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)));
7b5445e7 302 /* Remove the previous entry. */
73a1a6f4
DDM
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)));
7b5445e7 305 /* Then, set a different address. */
73a1a6f4
DDM
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)));
7b5445e7 308 /* Remove again. */
73a1a6f4
DDM
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)));
7b5445e7 311 /* Try to remove non-existent entry. */
73a1a6f4
DDM
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)));
7b5445e7 314 /* Try to remove non-existent entry. */
73a1a6f4
DDM
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)));
7b5445e7
YW
317}
318
b44cd882 319int main(int argc, char *argv[]) {
6f08fb7b 320 int r;
b44cd882 321
6d7c4033 322 test_setup_logging(LOG_DEBUG);
b44cd882 323
4f3cb246 324 test_client_id_hash();
7b5445e7 325 test_static_lease();
4f3cb246 326
4e2319af 327 r = test_basic(true);
7e0a8bf1
YW
328 if (r < 0)
329 return log_tests_skipped_errno(r, "cannot start dhcp server(bound to interface)");
21b6b87e 330
4e2319af 331 r = test_basic(false);
7e0a8bf1
YW
332 if (r < 0)
333 return log_tests_skipped_errno(r, "cannot start dhcp server(non-bound to interface)");
6f08fb7b 334
be077570 335 test_message_handler();
b44cd882
TG
336
337 return 0;
338}