1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/ztest.h>
8 #include <zephyr/net/dummy.h>
9 #include <zephyr/net/ethernet.h>
10 #include <zephyr/net/icmp.h>
11 #include <zephyr/net/net_ip.h>
12 #include <zephyr/net/dhcpv4.h>
13 #include <zephyr/net/dhcpv4_server.h>
14
15 #include "dhcpv4/dhcpv4_internal.h"
16 #include "icmpv4.h"
17 #include "ipv4.h"
18 #include "udp_internal.h"
19
20 /* 00-00-5E-00-53-xx Documentation RFC 7042 */
21 static uint8_t server_mac_addr[] = { 0x00, 0x00, 0x5E, 0x00, 0x53, 0x01 };
22 static uint8_t client_mac_addr[] = { 0x00, 0x00, 0x5E, 0x00, 0x53, 0x02 };
23
24 static struct in_addr server_addr = { { { 192, 0, 2, 1 } } };
25 static struct in_addr netmask = { { { 255, 255, 255, 0 } } };
26 static struct in_addr test_base_addr = { { { 192, 0, 2, 10 } } };
27
28 /* Only to test Inform. */
29 static struct in_addr client_addr_static = { { { 192, 0, 2, 2 } } };
30
31 typedef void (*test_dhcpv4_server_fn_t)(struct net_if *iface,
32 struct net_pkt *pkt);
33
34
35 static struct test_dhcpv4_server_ctx {
36 struct net_if *iface;
37 struct k_sem test_proceed;
38 struct net_pkt *pkt;
39 struct in_addr assigned_ip;
40 struct in_addr declined_ip;
41
42 /* Request params */
43 const char *client_id;
44 int lease_time;
45 bool broadcast;
46 bool send_echo_reply;
47 } test_ctx;
48
49 struct test_lease_count {
50 int reserved;
51 int allocated;
52 int declined;
53 };
54
55 #define CLIENT_ID_1 "client1"
56 #define CLIENT_ID_2 "client2"
57 #define NO_LEASE_TIME -1
58 #define TEST_XID 0x12345678
59
60 #define TEST_TIMEOUT K_MSEC(100)
61
server_iface_init(struct net_if * iface)62 static void server_iface_init(struct net_if *iface)
63 {
64 net_if_set_link_addr(iface, server_mac_addr, sizeof(server_mac_addr),
65 NET_LINK_ETHERNET);
66
67 test_ctx.iface = iface;
68
69 (void)net_if_ipv4_addr_add(iface, &server_addr, NET_ADDR_MANUAL, 0);
70 (void)net_if_ipv4_set_netmask_by_addr(iface, &server_addr, &netmask);
71 }
72
send_icmp_echo_reply(struct net_pkt * pkt,struct net_ipv4_hdr * ipv4_hdr)73 static void send_icmp_echo_reply(struct net_pkt *pkt,
74 struct net_ipv4_hdr *ipv4_hdr)
75 {
76 struct net_pkt *reply;
77 size_t payload_len = net_pkt_get_len(pkt) - net_pkt_ip_hdr_len(pkt) -
78 NET_ICMPH_LEN;
79
80 reply = net_pkt_alloc_with_buffer(net_pkt_iface(pkt), payload_len,
81 AF_INET, IPPROTO_ICMP, K_FOREVER);
82 zassert_not_null(reply, "Failed to allocate echo reply");
83
84 zassert_ok(net_ipv4_create(reply, (struct in_addr *)ipv4_hdr->dst,
85 (struct in_addr *)ipv4_hdr->src),
86 "Failed to create IPv4 header");
87
88 zassert_ok(net_icmpv4_create(reply, NET_ICMPV4_ECHO_REPLY, 0),
89 "Failed to create ICMP header");
90 zassert_ok(net_pkt_copy(reply, pkt, payload_len),
91 "Failed to copy payload");
92
93 net_pkt_cursor_init(reply);
94 net_ipv4_finalize(reply, IPPROTO_ICMP);
95
96 zassert_ok(net_recv_data(test_ctx.iface, reply), "Failed to receive data");
97 }
98
server_send(const struct device * dev,struct net_pkt * pkt)99 static int server_send(const struct device *dev, struct net_pkt *pkt)
100 {
101 NET_PKT_DATA_ACCESS_DEFINE(ipv4_access, struct net_ipv4_hdr);
102 struct net_ipv4_hdr *ipv4_hdr;
103
104 ipv4_hdr = (struct net_ipv4_hdr *)net_pkt_get_data(pkt, &ipv4_access);
105 zassert_not_null(ipv4_hdr, "Failed to access IPv4 header.");
106
107 if (ipv4_hdr->proto == IPPROTO_ICMP) {
108 if (test_ctx.send_echo_reply) {
109 test_ctx.send_echo_reply = false;
110 memcpy(&test_ctx.declined_ip, ipv4_hdr->dst,
111 sizeof(struct in_addr));
112 send_icmp_echo_reply(pkt, ipv4_hdr);
113 }
114
115 return 0;
116 }
117
118 test_ctx.pkt = pkt;
119 net_pkt_ref(pkt);
120
121 k_sem_give(&test_ctx.test_proceed);
122
123 return 0;
124 }
125
126 static struct dummy_api server_if_api = {
127 .iface_api.init = server_iface_init,
128 .send = server_send,
129 };
130
131 NET_DEVICE_INIT(server_iface, "server_iface", NULL, NULL, NULL, NULL,
132 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &server_if_api,
133 DUMMY_L2, NET_L2_GET_CTX_TYPE(DUMMY_L2), NET_IPV4_MTU);
134
135 static const uint8_t cookie[4] = { 0x63, 0x82, 0x53, 0x63 };
136
test_pkt_free(void)137 static void test_pkt_free(void)
138 {
139 if (test_ctx.pkt != NULL) {
140 net_pkt_unref(test_ctx.pkt);
141 test_ctx.pkt = NULL;
142 }
143 }
144
client_prepare_test_msg(const struct in_addr * src_addr,const struct in_addr * dst_addr,enum net_dhcpv4_msg_type type,const struct in_addr * server_id,const struct in_addr * requested_ip,const struct in_addr * ciaddr)145 static void client_prepare_test_msg(
146 const struct in_addr *src_addr, const struct in_addr *dst_addr,
147 enum net_dhcpv4_msg_type type, const struct in_addr *server_id,
148 const struct in_addr *requested_ip, const struct in_addr *ciaddr)
149 {
150 struct dhcp_msg msg = { 0 };
151 uint8_t empty_buf[SIZE_OF_FILE] = { 0 };
152 struct net_pkt *pkt;
153
154 pkt = net_pkt_alloc_with_buffer(test_ctx.iface, NET_IPV4_MTU, AF_INET,
155 IPPROTO_UDP, K_FOREVER);
156 zassert_not_null(pkt, "Failed to allocate packet");
157
158 net_pkt_set_ipv4_ttl(pkt, 1);
159
160 zassert_ok(net_ipv4_create(pkt, src_addr, dst_addr),
161 "Failed to create IPv4 header");
162 zassert_ok(net_udp_create(pkt, htons(DHCPV4_CLIENT_PORT),
163 htons(DHCPV4_SERVER_PORT)),
164 "Failed to create UDP header");
165
166 msg.op = DHCPV4_MSG_BOOT_REQUEST;
167 msg.htype = HARDWARE_ETHERNET_TYPE;
168 msg.hlen = sizeof(client_mac_addr);
169 msg.xid = htonl(TEST_XID);
170 if (test_ctx.broadcast) {
171 msg.flags = htons(DHCPV4_MSG_BROADCAST);
172 }
173
174 if (ciaddr) {
175 memcpy(msg.ciaddr, ciaddr, sizeof(*ciaddr));
176 } else {
177 memset(msg.ciaddr, 0, sizeof(msg.ciaddr));
178
179 }
180 memset(msg.yiaddr, 0, sizeof(msg.ciaddr));
181 memset(msg.siaddr, 0, sizeof(msg.siaddr));
182 memset(msg.giaddr, 0, sizeof(msg.giaddr));
183 memcpy(msg.chaddr, client_mac_addr, sizeof(client_mac_addr));
184
185 net_pkt_write(pkt, &msg, sizeof(msg));
186 net_pkt_write(pkt, empty_buf, SIZE_OF_SNAME);
187 net_pkt_write(pkt, empty_buf, SIZE_OF_FILE);
188 net_pkt_write(pkt, cookie, SIZE_OF_MAGIC_COOKIE);
189
190 /* Options */
191 net_pkt_write_u8(pkt, DHCPV4_OPTIONS_MSG_TYPE);
192 net_pkt_write_u8(pkt, 1);
193 net_pkt_write_u8(pkt, type);
194
195 if (requested_ip) {
196 net_pkt_write_u8(pkt, DHCPV4_OPTIONS_REQ_IPADDR);
197 net_pkt_write_u8(pkt, sizeof(*requested_ip));
198 net_pkt_write(pkt, requested_ip, sizeof(*requested_ip));
199 }
200
201 if (server_id) {
202 net_pkt_write_u8(pkt, DHCPV4_OPTIONS_SERVER_ID);
203 net_pkt_write_u8(pkt, sizeof(*server_id));
204 net_pkt_write(pkt, server_id, sizeof(*server_id));
205 }
206
207 net_pkt_write_u8(pkt, DHCPV4_OPTIONS_REQ_LIST);
208 net_pkt_write_u8(pkt, 1);
209 net_pkt_write_u8(pkt, DHCPV4_OPTIONS_SUBNET_MASK);
210
211 if (test_ctx.client_id) {
212 net_pkt_write_u8(pkt, DHCPV4_OPTIONS_CLIENT_ID);
213 net_pkt_write_u8(pkt, strlen(test_ctx.client_id));
214 net_pkt_write(pkt, test_ctx.client_id, strlen(test_ctx.client_id));
215 }
216
217 if (test_ctx.lease_time != NO_LEASE_TIME) {
218 net_pkt_write_u8(pkt, DHCPV4_OPTIONS_LEASE_TIME);
219 net_pkt_write_u8(pkt, 4);
220 net_pkt_write_be32(pkt, test_ctx.lease_time);
221 }
222
223 net_pkt_write_u8(pkt, DHCPV4_OPTIONS_END);
224
225 net_pkt_cursor_init(pkt);
226 net_ipv4_finalize(pkt, IPPROTO_UDP);
227
228 zassert_ok(net_recv_data(test_ctx.iface, pkt), "Failed to receive data");
229 }
230
client_send_discover(void)231 static void client_send_discover(void)
232 {
233 int ret;
234
235 client_prepare_test_msg(
236 net_ipv4_unspecified_address(), net_ipv4_broadcast_address(),
237 NET_DHCPV4_MSG_TYPE_DISCOVER, NULL, NULL, NULL);
238
239 /* Wait for reply */
240 ret = k_sem_take(&test_ctx.test_proceed, TEST_TIMEOUT);
241 zassert_ok(ret, "Exchange not completed in required time");
242 }
243
client_send_request_solicit(void)244 static void client_send_request_solicit(void)
245 {
246 int ret;
247
248 client_prepare_test_msg(
249 net_ipv4_unspecified_address(), net_ipv4_broadcast_address(),
250 NET_DHCPV4_MSG_TYPE_REQUEST, &server_addr, &test_ctx.assigned_ip,
251 NULL);
252
253 /* Wait for reply */
254 ret = k_sem_take(&test_ctx.test_proceed, TEST_TIMEOUT);
255 zassert_ok(ret, "Exchange not completed in required time");
256 }
257
client_send_request_renew(void)258 static void client_send_request_renew(void)
259 {
260 int ret;
261
262 client_prepare_test_msg(
263 &test_ctx.assigned_ip, &server_addr,
264 NET_DHCPV4_MSG_TYPE_REQUEST, NULL, NULL,
265 &test_ctx.assigned_ip);
266
267 /* Wait for reply */
268 ret = k_sem_take(&test_ctx.test_proceed, TEST_TIMEOUT);
269 zassert_ok(ret, "Exchange not completed in required time");
270 }
271
client_send_request_rebind(void)272 static void client_send_request_rebind(void)
273 {
274 int ret;
275
276 client_prepare_test_msg(
277 &test_ctx.assigned_ip, net_ipv4_broadcast_address(),
278 NET_DHCPV4_MSG_TYPE_REQUEST, NULL, NULL,
279 &test_ctx.assigned_ip);
280
281 /* Wait for reply */
282 ret = k_sem_take(&test_ctx.test_proceed, TEST_TIMEOUT);
283 zassert_ok(ret, "Exchange not completed in required time");
284 }
285
client_send_release(void)286 static void client_send_release(void)
287 {
288 client_prepare_test_msg(
289 &test_ctx.assigned_ip, &server_addr,
290 NET_DHCPV4_MSG_TYPE_RELEASE, &server_addr, NULL,
291 &test_ctx.assigned_ip);
292
293 /* Small delay to let the DHCP server process the packet */
294 k_msleep(10);
295 }
296
client_send_decline(void)297 static void client_send_decline(void)
298 {
299 client_prepare_test_msg(
300 net_ipv4_unspecified_address(), net_ipv4_broadcast_address(),
301 NET_DHCPV4_MSG_TYPE_DECLINE, &server_addr,
302 &test_ctx.assigned_ip, NULL);
303
304 /* Small delay to let the DHCP server process the packet */
305 k_msleep(10);
306 }
307
client_send_inform(void)308 static void client_send_inform(void)
309 {
310 int ret;
311
312 client_prepare_test_msg(
313 &client_addr_static, net_ipv4_broadcast_address(),
314 NET_DHCPV4_MSG_TYPE_INFORM, NULL, NULL, &client_addr_static);
315
316 /* Wait for reply */
317 ret = k_sem_take(&test_ctx.test_proceed, TEST_TIMEOUT);
318 zassert_ok(ret, "Exchange not completed in required time");
319 }
320
lease_count_cb(struct net_if * iface,struct dhcpv4_addr_slot * lease,void * user_data)321 static void lease_count_cb(struct net_if *iface, struct dhcpv4_addr_slot *lease,
322 void *user_data)
323 {
324 struct test_lease_count *count = user_data;
325
326 switch (lease->state) {
327 case DHCPV4_SERVER_ADDR_RESERVED:
328 count->reserved++;
329 break;
330
331 case DHCPV4_SERVER_ADDR_ALLOCATED:
332 count->allocated++;
333 break;
334
335 case DHCPV4_SERVER_ADDR_DECLINED:
336 count->declined++;
337 break;
338
339 default:
340 break;
341 }
342 }
343
test_get_lease_count(struct test_lease_count * count)344 static void test_get_lease_count(struct test_lease_count *count)
345 {
346 int ret;
347
348 memset(count, 0, sizeof(*count));
349
350 ret = net_dhcpv4_server_foreach_lease(test_ctx.iface, lease_count_cb,
351 count);
352 zassert_ok(ret, "Failed to obtain lease count");
353 }
354
verify_lease_count(int reserved,int allocated,int declined)355 static void verify_lease_count(int reserved, int allocated, int declined)
356 {
357 struct test_lease_count count;
358
359 test_get_lease_count(&count);
360 zassert_equal(count.reserved, reserved,
361 "Incorrect %s count, expected %d got %d", "reserved",
362 reserved, count.reserved);
363 zassert_equal(count.allocated, allocated,
364 "Incorrect %s count, expected %d got %d", "allocated",
365 allocated, count.allocated);
366 zassert_equal(count.declined, declined,
367 "Incorrect %s count, expected %d got %d", "declined",
368 declined, count.declined);
369 }
370
get_reserved_cb(struct net_if * iface,struct dhcpv4_addr_slot * lease,void * user_data)371 static void get_reserved_cb(struct net_if *iface,
372 struct dhcpv4_addr_slot *lease,
373 void *user_data)
374 {
375 struct in_addr *reserved = user_data;
376
377 if (lease->state == DHCPV4_SERVER_ADDR_RESERVED) {
378 reserved->s_addr = lease->addr.s_addr;
379 }
380 }
381
get_reserved_address(struct in_addr * reserved)382 static void get_reserved_address(struct in_addr *reserved)
383 {
384 int ret;
385
386 ret = net_dhcpv4_server_foreach_lease(test_ctx.iface,
387 get_reserved_cb,
388 reserved);
389 zassert_ok(ret, "Failed to obtain reserved address");
390 }
391
client_get_lease(bool verify)392 static void client_get_lease(bool verify)
393 {
394 client_send_discover();
395 if (verify) {
396 verify_lease_count(1, 0, 0);
397 }
398 get_reserved_address(&test_ctx.assigned_ip);
399 test_pkt_free();
400
401 client_send_request_solicit();
402 if (verify) {
403 verify_lease_count(0, 1, 0);
404 }
405 test_pkt_free();
406 }
407
verify_no_option(struct net_pkt * pkt,uint8_t opt_type)408 static void verify_no_option(struct net_pkt *pkt, uint8_t opt_type)
409 {
410 struct net_pkt_cursor cursor;
411
412 net_pkt_cursor_backup(pkt, &cursor);
413
414 while (true) {
415 uint8_t type;
416 uint8_t len;
417
418 if (net_pkt_read_u8(pkt, &type) < 0) {
419 break;
420 }
421
422 if (net_pkt_read_u8(pkt, &len) < 0) {
423 break;
424 }
425
426 zassert_not_equal(type, opt_type,
427 "Option %d should not be present", opt_type);
428
429 (void)net_pkt_skip(pkt, len);
430 }
431
432 net_pkt_cursor_restore(pkt, &cursor);
433 }
434
verify_option(struct net_pkt * pkt,uint8_t opt_type,const void * optval,uint8_t optlen)435 static void verify_option(struct net_pkt *pkt, uint8_t opt_type,
436 const void *optval, uint8_t optlen)
437 {
438 struct net_pkt_cursor cursor;
439
440 net_pkt_cursor_backup(pkt, &cursor);
441
442 while (true) {
443 static uint8_t buf[255];
444 uint8_t type;
445 uint8_t len;
446
447 if (net_pkt_read_u8(pkt, &type) < 0) {
448 break;
449 }
450
451 if (net_pkt_read_u8(pkt, &len) < 0) {
452 break;
453 }
454
455 if (net_pkt_read(pkt, buf, len)) {
456 break;
457 }
458
459 if (type == opt_type) {
460 zassert_equal(len, optlen, "Invalid option length");
461 zassert_mem_equal(buf, optval, optlen, "Invalid option value");
462
463 net_pkt_cursor_restore(pkt, &cursor);
464 return;
465 }
466 }
467
468 zassert_true(false, "Option %d not found", opt_type);
469 }
470
verify_option_uint32(struct net_pkt * pkt,uint8_t opt_type,uint32_t optval)471 static void verify_option_uint32(struct net_pkt *pkt, uint8_t opt_type,
472 uint32_t optval)
473 {
474 optval = htonl(optval);
475
476 verify_option(pkt, opt_type, &optval, sizeof(optval));
477 }
478
verify_option_uint8(struct net_pkt * pkt,uint8_t opt_type,uint8_t optval)479 static void verify_option_uint8(struct net_pkt *pkt, uint8_t opt_type,
480 uint8_t optval)
481 {
482 verify_option(pkt, opt_type, &optval, sizeof(optval));
483 }
484
verify_offer(bool broadcast)485 static void verify_offer(bool broadcast)
486 {
487 NET_PKT_DATA_ACCESS_DEFINE(ipv4_access, struct net_ipv4_hdr);
488 NET_PKT_DATA_ACCESS_DEFINE(udp_access, struct net_udp_hdr);
489 NET_PKT_DATA_ACCESS_DEFINE(dhcp_access, struct dhcp_msg);
490 uint8_t cookie_buf[SIZE_OF_MAGIC_COOKIE];
491 struct net_pkt *pkt = test_ctx.pkt;
492 struct net_ipv4_hdr *ipv4_hdr;
493 struct net_udp_hdr *udp_hdr;
494 struct dhcp_msg *msg;
495 int ret;
496
497 ipv4_hdr = (struct net_ipv4_hdr *)net_pkt_get_data(pkt, &ipv4_access);
498 zassert_not_null(ipv4_hdr, "Failed to access IPv4 header.");
499 net_pkt_acknowledge_data(pkt, &ipv4_access);
500
501 udp_hdr = (struct net_udp_hdr *)net_pkt_get_data(pkt, &udp_access);
502 zassert_not_null(udp_hdr, "Failed to access UDP header.");
503 net_pkt_acknowledge_data(pkt, &udp_access);
504
505 msg = (struct dhcp_msg *)net_pkt_get_data(pkt, &dhcp_access);
506 zassert_not_null(msg, "Failed to access DHCP data.");
507 net_pkt_acknowledge_data(pkt, &dhcp_access);
508
509 /* IPv4 */
510 zassert_mem_equal(ipv4_hdr->src, server_addr.s4_addr,
511 sizeof(struct in_addr), "Incorrect source address");
512 if (broadcast) {
513 zassert_mem_equal(ipv4_hdr->dst, net_ipv4_broadcast_address(),
514 sizeof(struct in_addr),
515 "Destination should be broadcast");
516 } else {
517 zassert_mem_equal(ipv4_hdr->dst, msg->yiaddr,
518 sizeof(struct in_addr),
519 "Destination should match address lease");
520 }
521 zassert_equal(ipv4_hdr->proto, IPPROTO_UDP, "Wrong protocol");
522
523 /* UDP */
524 zassert_equal(udp_hdr->src_port, htons(DHCPV4_SERVER_PORT),
525 "Wrong source port");
526 zassert_equal(udp_hdr->dst_port, htons(DHCPV4_CLIENT_PORT),
527 "Wrong client port");
528
529 /* DHCPv4 */
530 zassert_equal(msg->op, DHCPV4_MSG_BOOT_REPLY, "Incorrect %s value", "op");
531 zassert_equal(msg->htype, HARDWARE_ETHERNET_TYPE, "Incorrect %s value", "htype");
532 zassert_equal(msg->hlen, sizeof(client_mac_addr), "Incorrect %s value", "hlen");
533 zassert_equal(msg->hops, 0, "Incorrect %s value", "hops");
534 zassert_equal(msg->xid, htonl(TEST_XID), "Incorrect %s value", "xid");
535 zassert_equal(msg->secs, 0, "Incorrect %s value", "secs");
536 zassert_equal(sys_get_be32(msg->ciaddr), 0, "Incorrect %s value", "ciaddr");
537 zassert_true((sys_get_be32(msg->yiaddr) >=
538 sys_get_be32(test_base_addr.s4_addr)) &&
539 (sys_get_be32(msg->yiaddr) <
540 sys_get_be32(test_base_addr.s4_addr) +
541 CONFIG_NET_DHCPV4_SERVER_ADDR_COUNT),
542 "Assigned DHCP address outside of address pool");
543 zassert_equal(sys_get_be32(msg->siaddr), 0, "Incorrect %s value", "siaddr");
544 if (broadcast) {
545 zassert_equal(msg->flags, htons(DHCPV4_MSG_BROADCAST),
546 "Incorrect %s value", "flags");
547 } else {
548 zassert_equal(msg->flags, 0, "Incorrect %s value", "flags");
549 }
550 zassert_equal(sys_get_be32(msg->giaddr), 0, "Incorrect %s value", "giaddr");
551 zassert_mem_equal(msg->chaddr, client_mac_addr, sizeof(client_mac_addr),
552 "Incorrect %s value", "chaddr");
553
554 memcpy(&test_ctx.assigned_ip, msg->yiaddr, sizeof(struct in_addr));
555
556 ret = net_pkt_skip(pkt, SIZE_OF_SNAME + SIZE_OF_FILE);
557 zassert_ok(ret, "DHCP Offer too short");
558
559 ret = net_pkt_read(pkt, cookie_buf, SIZE_OF_MAGIC_COOKIE);
560 zassert_ok(ret, "DHCP Offer too short");
561 zassert_mem_equal(cookie_buf, cookie, SIZE_OF_MAGIC_COOKIE,
562 "Incorrect cookie value");
563
564 verify_option_uint32(pkt, DHCPV4_OPTIONS_LEASE_TIME,
565 CONFIG_NET_DHCPV4_SERVER_ADDR_LEASE_TIME);
566 verify_option_uint8(pkt, DHCPV4_OPTIONS_MSG_TYPE,
567 NET_DHCPV4_MSG_TYPE_OFFER);
568 verify_option(pkt, DHCPV4_OPTIONS_SERVER_ID, server_addr.s4_addr,
569 sizeof(struct in_addr));
570 verify_option(pkt, DHCPV4_OPTIONS_CLIENT_ID, test_ctx.client_id,
571 strlen(test_ctx.client_id));
572 verify_option(pkt, DHCPV4_OPTIONS_SUBNET_MASK, netmask.s4_addr,
573 sizeof(struct in_addr));
574 verify_no_option(pkt, DHCPV4_OPTIONS_REQ_IPADDR);
575 verify_no_option(pkt, DHCPV4_OPTIONS_REQ_LIST);
576 }
577
reserved_address_cb(struct net_if * iface,struct dhcpv4_addr_slot * lease,void * user_data)578 static void reserved_address_cb(struct net_if *iface,
579 struct dhcpv4_addr_slot *lease,
580 void *user_data)
581 {
582 struct in_addr *reserved = user_data;
583
584 zassert_equal(lease->state, DHCPV4_SERVER_ADDR_RESERVED,
585 "Wrong lease state");
586 zassert_equal(reserved->s_addr, lease->addr.s_addr,
587 "Reserved wrong address");
588 }
589
verify_reserved_address(struct in_addr * reserved)590 static void verify_reserved_address(struct in_addr *reserved)
591 {
592 int ret;
593
594 ret = net_dhcpv4_server_foreach_lease(test_ctx.iface,
595 reserved_address_cb,
596 reserved);
597 zassert_ok(ret, "Failed to verify reserved address");
598 }
599
600 /* Verify that the DHCP server replies with Offer for a Discover message. */
ZTEST(dhcpv4_server_tests,test_discover)601 ZTEST(dhcpv4_server_tests, test_discover)
602 {
603 client_send_discover();
604 verify_offer(false);
605 test_pkt_free();
606
607 verify_lease_count(1, 0, 0);
608 verify_reserved_address(&test_ctx.assigned_ip);
609 }
610
611 /* Verify that the DHCP server offers the same IP address for repeated Discover
612 * message.
613 */
ZTEST(dhcpv4_server_tests,test_discover_repeat)614 ZTEST(dhcpv4_server_tests, test_discover_repeat)
615 {
616 struct in_addr first_addr;
617
618 client_send_discover();
619 verify_offer(false);
620 test_pkt_free();
621
622 first_addr = test_ctx.assigned_ip;
623 verify_lease_count(1, 0, 0);
624
625 /* Repeat Discover with the same client ID */
626 client_send_discover();
627 verify_offer(false);
628 test_pkt_free();
629
630 verify_lease_count(1, 0, 0);
631 zassert_equal(first_addr.s_addr, test_ctx.assigned_ip.s_addr,
632 "Received different address for the same client ID");
633
634 /* Send Discover with a different client ID */
635 test_ctx.client_id = CLIENT_ID_2;
636
637 client_send_discover();
638 verify_offer(false);
639 test_pkt_free();
640
641 verify_lease_count(2, 0, 0);
642 zassert_not_equal(first_addr.s_addr, test_ctx.assigned_ip.s_addr,
643 "Received same address for the different client ID");
644 }
645
646 /* Verify that the DHCP server replies to broadcast address if broadcast flag
647 * is set.
648 */
ZTEST(dhcpv4_server_tests,test_discover_with_broadcast)649 ZTEST(dhcpv4_server_tests, test_discover_with_broadcast)
650 {
651 test_ctx.broadcast = true;
652
653 client_send_discover();
654 verify_offer(true);
655 verify_lease_count(1, 0, 0);
656 test_pkt_free();
657 }
658
verify_ack(bool inform,bool renew)659 static void verify_ack(bool inform, bool renew)
660 {
661 NET_PKT_DATA_ACCESS_DEFINE(ipv4_access, struct net_ipv4_hdr);
662 NET_PKT_DATA_ACCESS_DEFINE(udp_access, struct net_udp_hdr);
663 NET_PKT_DATA_ACCESS_DEFINE(dhcp_access, struct dhcp_msg);
664 uint8_t cookie_buf[SIZE_OF_MAGIC_COOKIE];
665 struct net_pkt *pkt = test_ctx.pkt;
666 struct net_ipv4_hdr *ipv4_hdr;
667 struct net_udp_hdr *udp_hdr;
668 struct dhcp_msg *msg;
669 int ret;
670
671 ipv4_hdr = (struct net_ipv4_hdr *)net_pkt_get_data(pkt, &ipv4_access);
672 zassert_not_null(ipv4_hdr, "Failed to access IPv4 header.");
673 net_pkt_acknowledge_data(pkt, &ipv4_access);
674
675 udp_hdr = (struct net_udp_hdr *)net_pkt_get_data(pkt, &udp_access);
676 zassert_not_null(udp_hdr, "Failed to access UDP header.");
677 net_pkt_acknowledge_data(pkt, &udp_access);
678
679 msg = (struct dhcp_msg *)net_pkt_get_data(pkt, &dhcp_access);
680 zassert_not_null(msg, "Failed to access DHCP data.");
681 net_pkt_acknowledge_data(pkt, &dhcp_access);
682
683 /* IPv4 */
684 zassert_mem_equal(ipv4_hdr->src, server_addr.s4_addr,
685 sizeof(struct in_addr), "Incorrect source address");
686 if (inform || renew) {
687 zassert_mem_equal(ipv4_hdr->dst, msg->ciaddr, sizeof(struct in_addr),
688 "Destination should match client address");
689 } else {
690 zassert_mem_equal(ipv4_hdr->dst, msg->yiaddr, sizeof(struct in_addr),
691 "Destination should match client address");
692 }
693
694 zassert_equal(ipv4_hdr->proto, IPPROTO_UDP, "Wrong protocol");
695
696 /* UDP */
697 zassert_equal(udp_hdr->src_port, htons(DHCPV4_SERVER_PORT),
698 "Wrong source port");
699 zassert_equal(udp_hdr->dst_port, htons(DHCPV4_CLIENT_PORT),
700 "Wrong client port");
701
702 /* DHCPv4 */
703 zassert_equal(msg->op, DHCPV4_MSG_BOOT_REPLY, "Incorrect %s value", "op");
704 zassert_equal(msg->htype, HARDWARE_ETHERNET_TYPE, "Incorrect %s value", "htype");
705 zassert_equal(msg->hlen, sizeof(client_mac_addr), "Incorrect %s value", "hlen");
706 zassert_equal(msg->hops, 0, "Incorrect %s value", "hops");
707 zassert_equal(msg->xid, htonl(TEST_XID), "Incorrect %s value", "xid");
708 zassert_equal(msg->secs, 0, "Incorrect %s value", "secs");
709
710 if (inform) {
711 zassert_mem_equal(msg->ciaddr, client_addr_static.s4_addr,
712 sizeof(struct in_addr),
713 "Incorrect %s value", "ciaddr");
714 } else if (renew) {
715 zassert_mem_equal(msg->ciaddr, test_ctx.assigned_ip.s4_addr,
716 sizeof(struct in_addr),
717 "Incorrect %s value", "ciaddr");
718 } else {
719 zassert_equal(sys_get_be32(msg->ciaddr), 0, "Incorrect %s value", "ciaddr");
720 }
721
722 if (inform) {
723 zassert_equal(sys_get_be32(msg->yiaddr), 0, "Incorrect %s value", "yiaddr");
724 } else {
725 zassert_mem_equal(msg->yiaddr, test_ctx.assigned_ip.s4_addr,
726 sizeof(struct in_addr), "Incorrect %s value", "yiaddr");
727 }
728
729 zassert_equal(sys_get_be32(msg->siaddr), 0, "Incorrect %s value", "siaddr");
730 zassert_equal(msg->flags, 0, "Incorrect %s value", "flags");
731 zassert_equal(sys_get_be32(msg->giaddr), 0, "Incorrect %s value", "giaddr");
732 zassert_mem_equal(msg->chaddr, client_mac_addr, sizeof(client_mac_addr),
733 "Incorrect %s value", "chaddr");
734
735 if (!inform) {
736 memcpy(&test_ctx.assigned_ip, msg->yiaddr, sizeof(struct in_addr));
737 }
738
739 ret = net_pkt_skip(pkt, SIZE_OF_SNAME + SIZE_OF_FILE);
740 zassert_ok(ret, "DHCP Offer too short");
741
742 ret = net_pkt_read(pkt, cookie_buf, SIZE_OF_MAGIC_COOKIE);
743 zassert_ok(ret, "DHCP Offer too short");
744 zassert_mem_equal(cookie_buf, cookie, SIZE_OF_MAGIC_COOKIE,
745 "Incorrect cookie value");
746
747 if (inform) {
748 verify_no_option(pkt, DHCPV4_OPTIONS_LEASE_TIME);
749 } else {
750 verify_option_uint32(pkt, DHCPV4_OPTIONS_LEASE_TIME,
751 CONFIG_NET_DHCPV4_SERVER_ADDR_LEASE_TIME);
752 }
753
754 verify_option_uint8(pkt, DHCPV4_OPTIONS_MSG_TYPE,
755 NET_DHCPV4_MSG_TYPE_ACK);
756 verify_option(pkt, DHCPV4_OPTIONS_SERVER_ID, server_addr.s4_addr,
757 sizeof(struct in_addr));
758 if (inform) {
759 verify_no_option(pkt, DHCPV4_OPTIONS_CLIENT_ID);
760 } else {
761 verify_option(pkt, DHCPV4_OPTIONS_CLIENT_ID, test_ctx.client_id,
762 strlen(test_ctx.client_id));
763 }
764 verify_option(pkt, DHCPV4_OPTIONS_SUBNET_MASK, netmask.s4_addr,
765 sizeof(struct in_addr));
766 verify_no_option(pkt, DHCPV4_OPTIONS_REQ_IPADDR);
767 verify_no_option(pkt, DHCPV4_OPTIONS_REQ_LIST);
768 }
769
allocated_address_cb(struct net_if * iface,struct dhcpv4_addr_slot * lease,void * user_data)770 static void allocated_address_cb(struct net_if *iface,
771 struct dhcpv4_addr_slot *lease,
772 void *user_data)
773 {
774 struct in_addr *allocated = user_data;
775
776 zassert_equal(lease->state, DHCPV4_SERVER_ADDR_ALLOCATED,
777 "Wrong lease state");
778 zassert_equal(allocated->s_addr, lease->addr.s_addr,
779 "Reserved wrong address");
780 }
781
verify_allocated_address(struct in_addr * allocated)782 static void verify_allocated_address(struct in_addr *allocated)
783 {
784 int ret;
785
786 ret = net_dhcpv4_server_foreach_lease(test_ctx.iface,
787 allocated_address_cb,
788 allocated);
789 zassert_ok(ret, "Failed to verify allocated address");
790 }
791
792 /* Verify that the DHCP server replies with ACK for a Request message. */
ZTEST(dhcpv4_server_tests,test_request)793 ZTEST(dhcpv4_server_tests, test_request)
794 {
795 client_send_discover();
796 verify_offer(false);
797 verify_lease_count(1, 0, 0);
798 test_pkt_free();
799
800 client_send_request_solicit();
801 verify_ack(false, false);
802 verify_lease_count(0, 1, 0);
803 verify_allocated_address(&test_ctx.assigned_ip);
804 test_pkt_free();
805 }
806
807 /* Verify that the DHCP server replies with ACK for a Request message
808 * (renewing).
809 */
ZTEST(dhcpv4_server_tests,test_renew)810 ZTEST(dhcpv4_server_tests, test_renew)
811 {
812 client_get_lease(true);
813
814 client_send_request_renew();
815 verify_ack(false, true);
816 verify_lease_count(0, 1, 0);
817 test_pkt_free();
818 }
819
820 /* Verify that the DHCP server replies with ACK for a Request message
821 * (rebinding).
822 */
ZTEST(dhcpv4_server_tests,test_rebind)823 ZTEST(dhcpv4_server_tests, test_rebind)
824 {
825 client_get_lease(true);
826
827 client_send_request_rebind();
828 verify_ack(false, true);
829 verify_lease_count(0, 1, 0);
830 test_pkt_free();
831 }
832
833 /* Verify that the DHCP server lease expires after the lease timeout. */
ZTEST(dhcpv4_server_tests,test_expiry)834 ZTEST(dhcpv4_server_tests, test_expiry)
835 {
836 test_ctx.lease_time = 1;
837 client_get_lease(true);
838
839 /* Add extra 10ms to avoid race. */
840 k_msleep(1000 + 10);
841 verify_lease_count(0, 0, 0);
842 }
843
844 /* Verify that the DHCP server releases the lease after receiving Release
845 * message.
846 */
ZTEST(dhcpv4_server_tests,test_release)847 ZTEST(dhcpv4_server_tests, test_release)
848 {
849 client_get_lease(true);
850
851 client_send_release();
852 verify_lease_count(0, 0, 0);
853 }
854
declined_address_cb(struct net_if * iface,struct dhcpv4_addr_slot * lease,void * user_data)855 static void declined_address_cb(struct net_if *iface,
856 struct dhcpv4_addr_slot *lease,
857 void *user_data)
858 {
859 struct in_addr *declined = user_data;
860
861 zassert_equal(lease->state, DHCPV4_SERVER_ADDR_DECLINED,
862 "Wrong lease state");
863 zassert_equal(declined->s_addr, lease->addr.s_addr,
864 "Declined wrong address");
865 }
866
verify_declined_address(struct in_addr * declined)867 static void verify_declined_address(struct in_addr *declined)
868 {
869 int ret;
870
871 ret = net_dhcpv4_server_foreach_lease(test_ctx.iface,
872 declined_address_cb,
873 declined);
874 zassert_ok(ret, "Failed to verify declined address");
875 }
876
877 /* Verify that the DHCP server blocks the address after receiving Decline
878 * message, and gets released after configured time.
879 */
ZTEST(dhcpv4_server_tests,test_decline)880 ZTEST(dhcpv4_server_tests, test_decline)
881 {
882 client_get_lease(true);
883 verify_lease_count(0, 1, 0);
884
885 client_send_decline();
886 verify_lease_count(0, 0, 1);
887 verify_declined_address(&test_ctx.assigned_ip);
888
889 /* Add extra 10ms to avoid race. */
890 k_msleep(1000 + 10);
891 verify_lease_count(0, 0, 0);
892 }
893
894 /* Verify that if all of the address leases get blocked (due to conflict), the
895 * server will try to reuse the oldest blocked entry on Discovery.
896 */
ZTEST(dhcpv4_server_tests,test_declined_reuse)897 ZTEST(dhcpv4_server_tests, test_declined_reuse)
898 {
899 struct in_addr oldest_addr;
900
901 for (int i = 0; i < CONFIG_NET_DHCPV4_SERVER_ADDR_COUNT; i++) {
902 client_get_lease(false);
903 if (i == 0) {
904 oldest_addr = test_ctx.assigned_ip;
905 }
906 client_send_decline();
907 k_msleep(10);
908 }
909
910 verify_lease_count(0, 0, 4);
911
912 client_send_discover();
913 verify_offer(false);
914 verify_lease_count(1, 0, 3);
915 test_pkt_free();
916
917 client_send_request_solicit();
918 verify_ack(false, false);
919 verify_lease_count(0, 1, 3);
920 test_pkt_free();
921
922 zassert_equal(oldest_addr.s_addr, test_ctx.assigned_ip.s_addr,
923 "Should've reassing oldest declined address");
924 }
925
926 /* Verify that the DHCP server replies with ACK for a Inform message, w/o
927 * address assignment.
928 */
ZTEST(dhcpv4_server_tests,test_inform)929 ZTEST(dhcpv4_server_tests, test_inform)
930 {
931 client_send_inform();
932 verify_ack(true, false);
933 verify_lease_count(0, 0, 0);
934 }
935
after_probe_address_cb(struct net_if * iface,struct dhcpv4_addr_slot * lease,void * user_data)936 static void after_probe_address_cb(struct net_if *iface,
937 struct dhcpv4_addr_slot *lease,
938 void *user_data)
939 {
940 if (lease->state == DHCPV4_SERVER_ADDR_DECLINED) {
941 zassert_equal(test_ctx.declined_ip.s_addr, lease->addr.s_addr,
942 "Declined wrong address");
943 }
944
945 if (lease->state == DHCPV4_SERVER_ADDR_RESERVED) {
946 zassert_equal(test_ctx.assigned_ip.s_addr, lease->addr.s_addr,
947 "Reserved wrong address");
948 }
949 }
950
verify_address_after_probe(void)951 static void verify_address_after_probe(void)
952 {
953 int ret;
954
955 ret = net_dhcpv4_server_foreach_lease(test_ctx.iface,
956 after_probe_address_cb,
957 NULL);
958 zassert_ok(ret, "Failed to verify address after probe");
959 }
960
961 /* Verify that if the server detects conflict with ICMP probe, it assigns
962 * different address.
963 */
ZTEST(dhcpv4_server_tests,test_icmp_probe)964 ZTEST(dhcpv4_server_tests, test_icmp_probe)
965 {
966 if (CONFIG_NET_DHCPV4_SERVER_ICMP_PROBE_TIMEOUT == 0) {
967 ztest_test_skip();
968 }
969
970 test_ctx.send_echo_reply = true;
971
972 client_send_discover();
973 verify_offer(false);
974 test_pkt_free();
975
976 verify_lease_count(1, 0, 1);
977 zassert_not_equal(test_ctx.assigned_ip.s_addr,
978 test_ctx.declined_ip.s_addr,
979 "DHCPv4 srever offered conflicted address");
980 verify_address_after_probe();
981 }
982
983 /* Verify that the DHCP server can start and validate input properly. */
ZTEST(dhcpv4_server_tests_no_init,test_initialization)984 ZTEST(dhcpv4_server_tests_no_init, test_initialization)
985 {
986 struct in_addr base_addr_wrong_subnet = { { { 192, 0, 3, 10 } } };
987 struct in_addr base_addr_overlap = { { { 192, 0, 2, 1 } } };
988 int ret;
989
990 ret = net_dhcpv4_server_start(test_ctx.iface, &base_addr_wrong_subnet);
991 zassert_equal(ret, -EINVAL, "Started server for wrong subnet");
992
993 ret = net_dhcpv4_server_start(test_ctx.iface, &base_addr_overlap);
994 zassert_equal(ret, -EINVAL, "Started server for overlapping address");
995
996 ret = net_dhcpv4_server_start(test_ctx.iface, &test_base_addr);
997 zassert_ok(ret, "Failed to start server for valid address range");
998
999 net_dhcpv4_server_stop(test_ctx.iface);
1000 }
1001
dhcpv4_server_tests_before(void * fixture)1002 static void dhcpv4_server_tests_before(void *fixture)
1003 {
1004 ARG_UNUSED(fixture);
1005
1006 k_sem_init(&test_ctx.test_proceed, 0, 1);
1007 test_ctx.client_id = CLIENT_ID_1;
1008 test_ctx.broadcast = false;
1009 test_ctx.pkt = NULL;
1010 test_ctx.lease_time = NO_LEASE_TIME;
1011 test_ctx.send_echo_reply = false;
1012 memset(&test_ctx.assigned_ip, 0, sizeof(test_ctx.assigned_ip));
1013
1014 net_dhcpv4_server_start(test_ctx.iface, &test_base_addr);
1015 }
1016
dhcpv4_server_tests_after(void * fixture)1017 static void dhcpv4_server_tests_after(void *fixture)
1018 {
1019 ARG_UNUSED(fixture);
1020
1021 test_pkt_free();
1022
1023 net_dhcpv4_server_stop(test_ctx.iface);
1024 }
1025
1026 ZTEST_SUITE(dhcpv4_server_tests, NULL, NULL, dhcpv4_server_tests_before,
1027 dhcpv4_server_tests_after, NULL);
1028
1029 ZTEST_SUITE(dhcpv4_server_tests_no_init, NULL, NULL, NULL, NULL, NULL);
1030