1 /** @file
2  * @brief DHCPv4 server implementation
3  */
4 
5 /*
6  * Copyright (c) 2024 Nordic Semiconductor ASA
7  *
8  * SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/net/net_ip.h>
14 #include <zephyr/net/dhcpv4.h>
15 #include <zephyr/net/dhcpv4_server.h>
16 #include <zephyr/net/ethernet.h>
17 #include <zephyr/net/icmp.h>
18 #include <zephyr/net/socket.h>
19 #include <zephyr/net/socket_service.h>
20 #include <zephyr/sys/byteorder.h>
21 
22 LOG_MODULE_REGISTER(net_dhcpv4_server, CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL);
23 
24 #include "dhcpv4_internal.h"
25 #include "net_private.h"
26 #include "../../l2/ethernet/arp.h"
27 
28 #define DHCPV4_OPTIONS_MSG_TYPE_SIZE 3
29 #define DHCPV4_OPTIONS_IP_LEASE_TIME_SIZE 6
30 #define DHCPV4_OPTIONS_SERVER_ID_SIZE 6
31 #define DHCPV4_OPTIONS_SUBNET_MASK_SIZE 6
32 #define DHCPV4_OPTIONS_ROUTER_SIZE 6
33 #define DHCPV4_OPTIONS_DNS_SERVER_SIZE 6
34 #define DHCPV4_OPTIONS_CLIENT_ID_MIN_SIZE 2
35 
36 #define ADDRESS_RESERVED_TIMEOUT K_SECONDS(30)
37 #define ADDRESS_PROBE_TIMEOUT K_MSEC(CONFIG_NET_DHCPV4_SERVER_ICMP_PROBE_TIMEOUT)
38 #define ADDRESS_DECLINED_TIMEOUT K_SECONDS(CONFIG_NET_DHCPV4_SERVER_ADDR_DECLINE_TIME)
39 
40 #if (CONFIG_NET_DHCPV4_SERVER_ICMP_PROBE_TIMEOUT > 0)
41 #define DHCPV4_SERVER_ICMP_PROBE 1
42 #endif
43 
44 /* RFC 1497 [17] */
45 static const uint8_t magic_cookie[4] = { 0x63, 0x82, 0x53, 0x63 };
46 
47 #define DHCPV4_MAX_PARAMETERS_REQUEST_LEN 16
48 
49 struct dhcpv4_parameter_request_list {
50 	uint8_t list[DHCPV4_MAX_PARAMETERS_REQUEST_LEN];
51 	uint8_t count;
52 };
53 
54 struct dhcpv4_server_probe_ctx {
55 	struct net_icmp_ctx icmp_ctx;
56 	struct dhcp_msg discovery;
57 	struct dhcpv4_parameter_request_list params;
58 	struct dhcpv4_client_id client_id;
59 	struct dhcpv4_addr_slot *slot;
60 };
61 
62 struct dhcpv4_server_ctx {
63 	struct net_if *iface;
64 	int sock;
65 	struct k_work_delayable timeout_work;
66 	struct dhcpv4_addr_slot addr_pool[CONFIG_NET_DHCPV4_SERVER_ADDR_COUNT];
67 	struct in_addr server_addr;
68 	struct in_addr netmask;
69 #if defined(DHCPV4_SERVER_ICMP_PROBE)
70 	struct dhcpv4_server_probe_ctx probe_ctx;
71 #endif
72 };
73 
74 static void *address_provider_callback_user_data;
75 static net_dhcpv4_server_provider_cb_t address_provider_callback;
76 static struct dhcpv4_server_ctx server_ctx[CONFIG_NET_DHCPV4_SERVER_INSTANCES];
77 static struct zsock_pollfd fds[CONFIG_NET_DHCPV4_SERVER_INSTANCES];
78 static K_MUTEX_DEFINE(server_lock);
79 
dhcpv4_server_timeout_recalc(struct dhcpv4_server_ctx * ctx)80 static void dhcpv4_server_timeout_recalc(struct dhcpv4_server_ctx *ctx)
81 {
82 	k_timepoint_t next = sys_timepoint_calc(K_FOREVER);
83 	k_timeout_t timeout;
84 
85 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
86 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
87 
88 		if (slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
89 		    slot->state == DHCPV4_SERVER_ADDR_ALLOCATED ||
90 		    slot->state == DHCPV4_SERVER_ADDR_DECLINED) {
91 			if (sys_timepoint_cmp(slot->expiry, next) < 0) {
92 				next = slot->expiry;
93 			}
94 		}
95 	}
96 
97 	timeout = sys_timepoint_timeout(next);
98 
99 	if (K_TIMEOUT_EQ(timeout, K_FOREVER)) {
100 		LOG_DBG("No more addresses, canceling timer");
101 		k_work_cancel_delayable(&ctx->timeout_work);
102 	} else {
103 		k_work_reschedule(&ctx->timeout_work, timeout);
104 	}
105 }
106 
107 /* Option parsing. */
108 
dhcpv4_find_option(uint8_t * data,size_t datalen,uint8_t * optlen,uint8_t opt_code)109 static uint8_t *dhcpv4_find_option(uint8_t *data, size_t datalen,
110 				   uint8_t *optlen, uint8_t opt_code)
111 {
112 	uint8_t *opt = NULL;
113 
114 	while (datalen > 0) {
115 		uint8_t code;
116 		uint8_t len;
117 
118 		code = *data;
119 
120 		/* Two special cases (fixed sized options) */
121 		if (code == 0) {
122 			data++;
123 			datalen--;
124 			continue;
125 		}
126 
127 		if (code == DHCPV4_OPTIONS_END) {
128 			break;
129 		}
130 
131 		/* Length field should now follow. */
132 		if (datalen < 2) {
133 			break;
134 		}
135 
136 		len = *(data + 1);
137 
138 		if (datalen < len + 2) {
139 			break;
140 		}
141 
142 		if (code == opt_code) {
143 			/* Found the option. */
144 			opt = data + 2;
145 			*optlen = len;
146 			break;
147 		}
148 
149 		data += len + 2;
150 		datalen -= len + 2;
151 	}
152 
153 	return opt;
154 }
155 
dhcpv4_find_message_type_option(uint8_t * data,size_t datalen,uint8_t * msgtype)156 static int dhcpv4_find_message_type_option(uint8_t *data, size_t datalen,
157 					   uint8_t *msgtype)
158 {
159 	uint8_t *opt;
160 	uint8_t optlen;
161 
162 	opt = dhcpv4_find_option(data, datalen, &optlen,
163 				 DHCPV4_OPTIONS_MSG_TYPE);
164 	if (opt == NULL) {
165 		return -ENOENT;
166 	}
167 
168 	if (optlen != 1) {
169 		return -EINVAL;
170 	}
171 
172 	*msgtype = *opt;
173 
174 	return 0;
175 }
176 
dhcpv4_find_server_id_option(uint8_t * data,size_t datalen,struct in_addr * server_id)177 static int dhcpv4_find_server_id_option(uint8_t *data, size_t datalen,
178 					struct in_addr *server_id)
179 {
180 	uint8_t *opt;
181 	uint8_t optlen;
182 
183 	opt = dhcpv4_find_option(data, datalen, &optlen,
184 				 DHCPV4_OPTIONS_SERVER_ID);
185 	if (opt == NULL) {
186 		return -ENOENT;
187 	}
188 
189 	if (optlen != sizeof(struct in_addr)) {
190 		return -EINVAL;
191 	}
192 
193 	memcpy(server_id, opt, sizeof(struct in_addr));
194 
195 	return 0;
196 }
197 
dhcpv4_find_client_id_option(uint8_t * data,size_t datalen,uint8_t * client_id,uint8_t * len)198 static int dhcpv4_find_client_id_option(uint8_t *data, size_t datalen,
199 					uint8_t *client_id, uint8_t *len)
200 {
201 	uint8_t *opt;
202 	uint8_t optlen;
203 
204 	opt = dhcpv4_find_option(data, datalen, &optlen,
205 				 DHCPV4_OPTIONS_CLIENT_ID);
206 	if (opt == NULL) {
207 		return -ENOENT;
208 	}
209 
210 	if (optlen < DHCPV4_OPTIONS_CLIENT_ID_MIN_SIZE) {
211 		return -EINVAL;
212 	}
213 
214 	if (optlen > *len) {
215 		LOG_ERR("Not enough memory for DHCPv4 client identifier.");
216 		return -ENOMEM;
217 	}
218 
219 	memcpy(client_id, opt, optlen);
220 	*len = optlen;
221 
222 	return 0;
223 }
224 
dhcpv4_find_requested_ip_option(uint8_t * data,size_t datalen,struct in_addr * requested_ip)225 static int dhcpv4_find_requested_ip_option(uint8_t *data, size_t datalen,
226 					   struct in_addr *requested_ip)
227 {
228 	uint8_t *opt;
229 	uint8_t optlen;
230 
231 	opt = dhcpv4_find_option(data, datalen, &optlen,
232 				 DHCPV4_OPTIONS_REQ_IPADDR);
233 	if (opt == NULL) {
234 		return -ENOENT;
235 	}
236 
237 	if (optlen != sizeof(struct in_addr)) {
238 		return -EINVAL;
239 	}
240 
241 	memcpy(requested_ip, opt, sizeof(struct in_addr));
242 
243 	return 0;
244 }
245 
dhcpv4_find_ip_lease_time_option(uint8_t * data,size_t datalen,uint32_t * lease_time)246 static int dhcpv4_find_ip_lease_time_option(uint8_t *data, size_t datalen,
247 					    uint32_t *lease_time)
248 {
249 	uint8_t *opt;
250 	uint8_t optlen;
251 
252 	opt = dhcpv4_find_option(data, datalen, &optlen,
253 				 DHCPV4_OPTIONS_LEASE_TIME);
254 	if (opt == NULL) {
255 		return -ENOENT;
256 	}
257 
258 	if (optlen != sizeof(uint32_t)) {
259 		return -EINVAL;
260 	}
261 
262 	*lease_time = sys_get_be32(opt);
263 
264 	return 0;
265 }
266 
dhcpv4_find_parameter_request_list_option(uint8_t * data,size_t datalen,struct dhcpv4_parameter_request_list * params)267 static int dhcpv4_find_parameter_request_list_option(
268 				uint8_t *data, size_t datalen,
269 				struct dhcpv4_parameter_request_list *params)
270 {
271 	uint8_t *opt;
272 	uint8_t optlen;
273 
274 	opt = dhcpv4_find_option(data, datalen, &optlen,
275 				 DHCPV4_OPTIONS_REQ_LIST);
276 	if (opt == NULL) {
277 		return -ENOENT;
278 	}
279 
280 	if (optlen > sizeof(params->list)) {
281 		/* Best effort here, copy as much as we can. */
282 		optlen = sizeof(params->list);
283 	}
284 
285 	memcpy(params->list, opt, optlen);
286 	params->count = optlen;
287 
288 	return 0;
289 }
290 
291 /* Option encoding. */
292 
dhcpv4_encode_magic_cookie(uint8_t * buf,size_t * buflen)293 static uint8_t *dhcpv4_encode_magic_cookie(uint8_t *buf, size_t *buflen)
294 {
295 	if (buf == NULL || *buflen < SIZE_OF_MAGIC_COOKIE) {
296 		return NULL;
297 	}
298 
299 	memcpy(buf, magic_cookie, SIZE_OF_MAGIC_COOKIE);
300 
301 	*buflen -= SIZE_OF_MAGIC_COOKIE;
302 
303 	return buf + SIZE_OF_MAGIC_COOKIE;
304 }
305 
dhcpv4_encode_ip_lease_time_option(uint8_t * buf,size_t * buflen,uint32_t lease_time)306 static uint8_t *dhcpv4_encode_ip_lease_time_option(uint8_t *buf, size_t *buflen,
307 						   uint32_t lease_time)
308 {
309 	if (buf == NULL || *buflen < DHCPV4_OPTIONS_IP_LEASE_TIME_SIZE) {
310 		return NULL;
311 	}
312 
313 	buf[0] = DHCPV4_OPTIONS_LEASE_TIME;
314 	buf[1] = sizeof(lease_time);
315 	sys_put_be32(lease_time, &buf[2]);
316 
317 	*buflen -= DHCPV4_OPTIONS_IP_LEASE_TIME_SIZE;
318 
319 	return buf + DHCPV4_OPTIONS_IP_LEASE_TIME_SIZE;
320 }
321 
dhcpv4_encode_message_type_option(uint8_t * buf,size_t * buflen,uint8_t msgtype)322 static uint8_t *dhcpv4_encode_message_type_option(uint8_t *buf, size_t *buflen,
323 						  uint8_t msgtype)
324 {
325 	if (buf == NULL || *buflen < DHCPV4_OPTIONS_MSG_TYPE_SIZE) {
326 		return NULL;
327 	}
328 
329 	buf[0] = DHCPV4_OPTIONS_MSG_TYPE;
330 	buf[1] = 1;
331 	buf[2] = msgtype;
332 
333 	*buflen -= DHCPV4_OPTIONS_MSG_TYPE_SIZE;
334 
335 	return buf + DHCPV4_OPTIONS_MSG_TYPE_SIZE;
336 }
337 
dhcpv4_encode_server_id_option(uint8_t * buf,size_t * buflen,struct in_addr * server_id)338 static uint8_t *dhcpv4_encode_server_id_option(uint8_t *buf, size_t *buflen,
339 					       struct in_addr *server_id)
340 {
341 	if (buf == NULL || *buflen < DHCPV4_OPTIONS_SERVER_ID_SIZE) {
342 		return NULL;
343 	}
344 
345 	buf[0] = DHCPV4_OPTIONS_SERVER_ID;
346 	buf[1] = sizeof(struct in_addr);
347 	memcpy(&buf[2], server_id->s4_addr, sizeof(struct in_addr));
348 
349 	*buflen -= DHCPV4_OPTIONS_SERVER_ID_SIZE;
350 
351 	return buf + DHCPV4_OPTIONS_SERVER_ID_SIZE;
352 }
353 
dhcpv4_encode_client_id_option(uint8_t * buf,size_t * buflen,struct dhcpv4_client_id * client_id)354 static uint8_t *dhcpv4_encode_client_id_option(uint8_t *buf, size_t *buflen,
355 					       struct dhcpv4_client_id *client_id)
356 {
357 	if (buf == NULL || *buflen < client_id->len + 2) {
358 		return NULL;
359 	}
360 
361 	buf[0] = DHCPV4_OPTIONS_CLIENT_ID;
362 	buf[1] = client_id->len;
363 	memcpy(&buf[2], client_id->buf, client_id->len);
364 
365 	*buflen -= client_id->len + 2;
366 
367 	return buf + client_id->len + 2;
368 }
369 
dhcpv4_encode_subnet_mask_option(uint8_t * buf,size_t * buflen,struct in_addr * mask)370 static uint8_t *dhcpv4_encode_subnet_mask_option(uint8_t *buf, size_t *buflen,
371 						 struct in_addr *mask)
372 {
373 	if (buf == NULL || *buflen < DHCPV4_OPTIONS_SUBNET_MASK_SIZE) {
374 		return NULL;
375 	}
376 
377 	buf[0] = DHCPV4_OPTIONS_SUBNET_MASK;
378 	buf[1] = sizeof(struct in_addr);
379 	memcpy(&buf[2], mask->s4_addr, sizeof(struct in_addr));
380 
381 	*buflen -= DHCPV4_OPTIONS_SUBNET_MASK_SIZE;
382 
383 	return buf + DHCPV4_OPTIONS_SUBNET_MASK_SIZE;
384 }
385 
dhcpv4_encode_router_option(uint8_t * buf,size_t * buflen,struct in_addr * router)386 static uint8_t *dhcpv4_encode_router_option(uint8_t *buf, size_t *buflen,
387 					    struct in_addr *router)
388 {
389 	if (buf == NULL || *buflen < DHCPV4_OPTIONS_ROUTER_SIZE) {
390 		return NULL;
391 	}
392 
393 	buf[0] = DHCPV4_OPTIONS_ROUTER;
394 	buf[1] = sizeof(struct in_addr);
395 	memcpy(&buf[2], router->s4_addr, sizeof(struct in_addr));
396 
397 	*buflen -= DHCPV4_OPTIONS_ROUTER_SIZE;
398 
399 	return buf + DHCPV4_OPTIONS_ROUTER_SIZE;
400 }
401 
dhcpv4_encode_dns_server_option(uint8_t * buf,size_t * buflen)402 static uint8_t *dhcpv4_encode_dns_server_option(uint8_t *buf, size_t *buflen)
403 {
404 	struct in_addr dns_address;
405 
406 	if (buf == NULL || *buflen < DHCPV4_OPTIONS_DNS_SERVER_SIZE) {
407 		return NULL;
408 	}
409 
410 	if (net_addr_pton(AF_INET, CONFIG_NET_DHCPV4_SERVER_OPTION_DNS_ADDRESS, &dns_address)) {
411 		LOG_ERR("Invalid DNS server address: %s",
412 			CONFIG_NET_DHCPV4_SERVER_OPTION_DNS_ADDRESS);
413 		return NULL;
414 	}
415 
416 	buf[0] = DHCPV4_OPTIONS_DNS_SERVER;
417 	buf[1] = sizeof(struct in_addr);
418 	memcpy(&buf[2], dns_address.s4_addr, sizeof(struct in_addr));
419 
420 	*buflen -= DHCPV4_OPTIONS_DNS_SERVER_SIZE;
421 
422 	return buf + DHCPV4_OPTIONS_DNS_SERVER_SIZE;
423 }
424 
dhcpv4_encode_end_option(uint8_t * buf,size_t * buflen)425 static uint8_t *dhcpv4_encode_end_option(uint8_t *buf, size_t *buflen)
426 {
427 	if (buf == NULL || *buflen < 1) {
428 		return NULL;
429 	}
430 
431 	buf[0] = DHCPV4_OPTIONS_END;
432 
433 	*buflen -= 1;
434 
435 	return buf + 1;
436 }
437 
438 /* Response handlers. */
439 
dhcpv4_encode_header(uint8_t * buf,size_t * buflen,struct dhcp_msg * msg,struct in_addr * yiaddr)440 static uint8_t *dhcpv4_encode_header(uint8_t *buf, size_t *buflen,
441 				     struct dhcp_msg *msg,
442 				     struct in_addr *yiaddr)
443 {
444 	struct dhcp_msg *reply_msg = (struct dhcp_msg *)buf;
445 
446 	if (buf == NULL || *buflen < sizeof(struct dhcp_msg)) {
447 		return NULL;
448 	}
449 
450 	reply_msg->op = DHCPV4_MSG_BOOT_REPLY;
451 	reply_msg->htype = msg->htype;
452 	reply_msg->hlen = msg->hlen;
453 	reply_msg->hops = 0;
454 	reply_msg->xid = msg->xid;
455 	reply_msg->secs = 0;
456 	reply_msg->flags = msg->flags;
457 	memcpy(reply_msg->ciaddr, msg->ciaddr, sizeof(reply_msg->ciaddr));
458 	if (yiaddr != NULL) {
459 		memcpy(reply_msg->yiaddr, yiaddr, sizeof(struct in_addr));
460 	} else {
461 		memset(reply_msg->yiaddr, 0, sizeof(reply_msg->ciaddr));
462 	}
463 	memset(reply_msg->siaddr, 0, sizeof(reply_msg->siaddr));
464 	memcpy(reply_msg->giaddr, msg->giaddr, sizeof(reply_msg->giaddr));
465 	memcpy(reply_msg->chaddr, msg->chaddr, sizeof(reply_msg->chaddr));
466 
467 	*buflen -= sizeof(struct dhcp_msg);
468 
469 	return buf + sizeof(struct dhcp_msg);
470 }
471 
dhcpv4_encode_string(uint8_t * buf,size_t * buflen,char * str,size_t max_len)472 static uint8_t *dhcpv4_encode_string(uint8_t *buf, size_t *buflen, char *str,
473 				     size_t max_len)
474 {
475 	if (buf == NULL || *buflen < max_len) {
476 		return NULL;
477 	}
478 
479 	memset(buf, 0, max_len);
480 
481 	if (str == NULL) {
482 		goto out;
483 	}
484 
485 	strncpy(buf, str, max_len - 1);
486 
487  out:
488 	*buflen -= max_len;
489 
490 	return buf + max_len;
491 }
492 
dhcpv4_encode_sname(uint8_t * buf,size_t * buflen,char * sname)493 static uint8_t *dhcpv4_encode_sname(uint8_t *buf, size_t *buflen, char *sname)
494 {
495 	return dhcpv4_encode_string(buf, buflen, sname, SIZE_OF_SNAME);
496 }
497 
dhcpv4_encode_file(uint8_t * buf,size_t * buflen,char * file)498 static uint8_t *dhcpv4_encode_file(uint8_t *buf, size_t *buflen, char *file)
499 {
500 	return dhcpv4_encode_string(buf, buflen, file, SIZE_OF_FILE);
501 }
502 
dhcpv4_encode_requested_params(uint8_t * buf,size_t * buflen,struct dhcpv4_server_ctx * ctx,struct dhcpv4_parameter_request_list * params)503 static uint8_t *dhcpv4_encode_requested_params(
504 				uint8_t *buf, size_t *buflen,
505 				struct dhcpv4_server_ctx *ctx,
506 				struct dhcpv4_parameter_request_list *params)
507 {
508 	for (uint8_t i = 0; i < params->count; i++) {
509 		switch (params->list[i]) {
510 		case DHCPV4_OPTIONS_SUBNET_MASK:
511 			buf = dhcpv4_encode_subnet_mask_option(
512 				buf, buflen, &ctx->netmask);
513 			if (buf == NULL) {
514 				goto out;
515 			}
516 			break;
517 
518 		case DHCPV4_OPTIONS_ROUTER:
519 			buf = dhcpv4_encode_router_option(
520 				buf, buflen, &ctx->iface->config.ip.ipv4->gw);
521 			if (buf == NULL) {
522 				goto out;
523 			}
524 			break;
525 
526 		case DHCPV4_OPTIONS_DNS_SERVER:
527 			buf = dhcpv4_encode_dns_server_option(buf, buflen);
528 			if (buf == NULL) {
529 				goto out;
530 			}
531 			break;
532 		/* Others - just ignore. */
533 		default:
534 			break;
535 		}
536 	}
537 
538 out:
539 	return buf;
540 }
541 
dhcpv4_send(struct dhcpv4_server_ctx * ctx,enum net_dhcpv4_msg_type type,uint8_t * reply,size_t len,struct dhcp_msg * msg,struct in_addr * yiaddr)542 static int dhcpv4_send(struct dhcpv4_server_ctx *ctx, enum net_dhcpv4_msg_type type,
543 		       uint8_t *reply, size_t len, struct dhcp_msg *msg,
544 		       struct in_addr *yiaddr)
545 {
546 	struct sockaddr_in dst_addr = {
547 		.sin_family = AF_INET,
548 		.sin_port = htons(DHCPV4_CLIENT_PORT),
549 	};
550 	struct in_addr giaddr; /* Relay agent address */
551 	struct in_addr ciaddr; /* Client address */
552 	int ret;
553 
554 	memcpy(&giaddr, msg->giaddr, sizeof(giaddr));
555 	memcpy(&ciaddr, msg->ciaddr, sizeof(ciaddr));
556 
557 	/* Select destination address as described in ch. 4.1. */
558 	if (!net_ipv4_is_addr_unspecified(&giaddr)) {
559 		/* If the 'giaddr' field in a DHCP message from a client is
560 		 * non-zero, the server sends any return messages to the
561 		 * 'DHCP server' port on the BOOTP relay agent whose address
562 		 * appears in 'giaddr'.
563 		 */
564 		dst_addr.sin_addr = giaddr;
565 		dst_addr.sin_port = htons(DHCPV4_SERVER_PORT);
566 	} else if (type == NET_DHCPV4_MSG_TYPE_NAK) {
567 		/* In all cases, when 'giaddr' is zero, the server broadcasts
568 		 * any DHCPNAK messages to 0xffffffff.
569 		 */
570 		dst_addr.sin_addr = *net_ipv4_broadcast_address();
571 	} else if (!net_ipv4_is_addr_unspecified(&ciaddr)) {
572 		/* If the 'giaddr' field is zero and the 'ciaddr' field is
573 		 * nonzero, then the server unicasts DHCPOFFER and DHCPACK
574 		 * messages to the address in 'ciaddr'.
575 		 */
576 		dst_addr.sin_addr = ciaddr;
577 	} else if (ntohs(msg->flags) & DHCPV4_MSG_BROADCAST) {
578 		/* If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast
579 		 * bit is set, then the server broadcasts DHCPOFFER and DHCPACK
580 		 * messages to 0xffffffff.
581 		 */
582 		dst_addr.sin_addr = *net_ipv4_broadcast_address();
583 	} else if (yiaddr != NULL) {
584 		/* If the broadcast bit is not set and 'giaddr' is zero and
585 		 * 'ciaddr' is zero, then the server unicasts DHCPOFFER and
586 		 * DHCPACK messages to the client's hardware address and 'yiaddr'
587 		 * address.
588 		 */
589 		struct net_eth_addr hwaddr;
590 
591 		memcpy(&hwaddr, msg->chaddr, sizeof(hwaddr));
592 		net_arp_update(ctx->iface, yiaddr, &hwaddr, false, true);
593 		dst_addr.sin_addr = *yiaddr;
594 	} else {
595 		NET_ERR("Unspecified destination address.");
596 		return -EDESTADDRREQ;
597 	}
598 
599 	ret = zsock_sendto(ctx->sock, reply, len, 0, (struct sockaddr *)&dst_addr,
600 			   sizeof(dst_addr));
601 	if (ret < 0) {
602 		return -errno;
603 	}
604 
605 	return 0;
606 }
607 
dhcpv4_send_offer(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,struct in_addr * addr,uint32_t lease_time,struct dhcpv4_parameter_request_list * params,struct dhcpv4_client_id * client_id)608 static int dhcpv4_send_offer(struct dhcpv4_server_ctx *ctx, struct dhcp_msg *msg,
609 			     struct in_addr *addr, uint32_t lease_time,
610 			     struct dhcpv4_parameter_request_list *params,
611 			     struct dhcpv4_client_id *client_id)
612 {
613 	uint8_t reply[NET_IPV4_MTU];
614 	uint8_t *buf = reply;
615 	size_t buflen = sizeof(reply);
616 	size_t reply_len = 0;
617 	int ret;
618 
619 	buf = dhcpv4_encode_header(buf, &buflen, msg, addr);
620 	buf = dhcpv4_encode_sname(buf, &buflen, NULL);
621 	buf = dhcpv4_encode_file(buf, &buflen, NULL);
622 	buf = dhcpv4_encode_magic_cookie(buf, &buflen);
623 	buf = dhcpv4_encode_ip_lease_time_option(buf, &buflen, lease_time);
624 	buf = dhcpv4_encode_message_type_option(buf, &buflen,
625 						NET_DHCPV4_MSG_TYPE_OFFER);
626 	buf = dhcpv4_encode_server_id_option(buf, &buflen, &ctx->server_addr);
627 	buf = dhcpv4_encode_client_id_option(buf, &buflen, client_id);
628 	buf = dhcpv4_encode_requested_params(buf, &buflen, ctx, params);
629 	buf = dhcpv4_encode_end_option(buf, &buflen);
630 
631 	if (buf == NULL) {
632 		LOG_ERR("Failed to encode %s message", "Offer");
633 		return -ENOMEM;
634 	}
635 
636 	reply_len = sizeof(reply) - buflen;
637 
638 	ret = dhcpv4_send(ctx, NET_DHCPV4_MSG_TYPE_OFFER, reply, reply_len,
639 			  msg, addr);
640 	if (ret < 0) {
641 		LOG_ERR("Failed to send %s message, %d", "Offer", ret);
642 		return ret;
643 	}
644 
645 	return 0;
646 }
647 
dhcpv4_send_ack(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,struct in_addr * addr,uint32_t lease_time,struct dhcpv4_parameter_request_list * params,struct dhcpv4_client_id * client_id,bool inform)648 static int dhcpv4_send_ack(struct dhcpv4_server_ctx *ctx, struct dhcp_msg *msg,
649 			   struct in_addr *addr, uint32_t lease_time,
650 			   struct dhcpv4_parameter_request_list *params,
651 			   struct dhcpv4_client_id *client_id,
652 			   bool inform)
653 {
654 	uint8_t reply[NET_IPV4_MTU];
655 	uint8_t *buf = reply;
656 	size_t buflen = sizeof(reply);
657 	size_t reply_len = 0;
658 	int ret;
659 
660 	buf = dhcpv4_encode_header(buf, &buflen, msg, inform ? NULL : addr);
661 	buf = dhcpv4_encode_sname(buf, &buflen, NULL);
662 	buf = dhcpv4_encode_file(buf, &buflen, NULL);
663 	buf = dhcpv4_encode_magic_cookie(buf, &buflen);
664 	if (!inform) {
665 		buf = dhcpv4_encode_ip_lease_time_option(buf, &buflen, lease_time);
666 	}
667 	buf = dhcpv4_encode_message_type_option(buf, &buflen,
668 						NET_DHCPV4_MSG_TYPE_ACK);
669 	buf = dhcpv4_encode_server_id_option(buf, &buflen, &ctx->server_addr);
670 	if (!inform) {
671 		buf = dhcpv4_encode_client_id_option(buf, &buflen, client_id);
672 	}
673 	buf = dhcpv4_encode_requested_params(buf, &buflen, ctx, params);
674 	buf = dhcpv4_encode_end_option(buf, &buflen);
675 
676 	if (buf == NULL) {
677 		LOG_ERR("Failed to encode %s message", "ACK");
678 		return -ENOMEM;
679 	}
680 
681 	reply_len = sizeof(reply) - buflen;
682 
683 	ret = dhcpv4_send(ctx, NET_DHCPV4_MSG_TYPE_ACK, reply, reply_len, msg,
684 			  addr);
685 	if (ret < 0) {
686 		LOG_ERR("Failed to send %s message, %d", "ACK", ret);
687 		return ret;
688 	}
689 
690 	return 0;
691 }
692 
dhcpv4_send_nak(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,struct dhcpv4_client_id * client_id)693 static int dhcpv4_send_nak(struct dhcpv4_server_ctx *ctx, struct dhcp_msg *msg,
694 			   struct dhcpv4_client_id *client_id)
695 {
696 	uint8_t reply[NET_IPV4_MTU];
697 	uint8_t *buf = reply;
698 	size_t buflen = sizeof(reply);
699 	size_t reply_len = 0;
700 	int ret;
701 
702 	buf = dhcpv4_encode_header(buf, &buflen, msg, NULL);
703 	buf = dhcpv4_encode_sname(buf, &buflen, NULL);
704 	buf = dhcpv4_encode_file(buf, &buflen, NULL);
705 	buf = dhcpv4_encode_magic_cookie(buf, &buflen);
706 	buf = dhcpv4_encode_message_type_option(buf, &buflen,
707 						NET_DHCPV4_MSG_TYPE_NAK);
708 	buf = dhcpv4_encode_server_id_option(buf, &buflen, &ctx->server_addr);
709 	buf = dhcpv4_encode_client_id_option(buf, &buflen, client_id);
710 	buf = dhcpv4_encode_end_option(buf, &buflen);
711 
712 	if (buf == NULL) {
713 		LOG_ERR("Failed to encode %s message", "NAK");
714 		return -ENOMEM;
715 	}
716 
717 	reply_len = sizeof(reply) - buflen;
718 
719 	ret = dhcpv4_send(ctx, NET_DHCPV4_MSG_TYPE_NAK, reply, reply_len, msg,
720 			  NULL);
721 	if (ret < 0) {
722 		LOG_ERR("Failed to send %s message, %d", "NAK", ret);
723 		return ret;
724 	}
725 
726 	return 0;
727 }
728 
729 /* Message handlers. */
730 
dhcpv4_get_client_id(struct dhcp_msg * msg,uint8_t * options,uint8_t optlen,struct dhcpv4_client_id * client_id)731 static int dhcpv4_get_client_id(struct dhcp_msg *msg, uint8_t *options,
732 				uint8_t optlen, struct dhcpv4_client_id *client_id)
733 {
734 	int ret;
735 
736 	client_id->len = sizeof(client_id->buf);
737 
738 	ret = dhcpv4_find_client_id_option(options, optlen, client_id->buf,
739 					   &client_id->len);
740 	if (ret == 0) {
741 		return 0;
742 	}
743 
744 	/* No Client Id option or too long to use, fallback to hardware address. */
745 	if (msg->hlen > sizeof(msg->chaddr)) {
746 		LOG_ERR("Malformed chaddr length.");
747 		return -EINVAL;
748 	}
749 
750 	client_id->buf[0] = msg->htype;
751 	memcpy(client_id->buf + 1, msg->chaddr, msg->hlen);
752 	client_id->len = msg->hlen + 1;
753 
754 	return 0;
755 }
756 
dhcpv4_get_lease_time(uint8_t * options,uint8_t optlen)757 static uint32_t dhcpv4_get_lease_time(uint8_t *options, uint8_t optlen)
758 {
759 	uint32_t lease_time;
760 
761 	if (dhcpv4_find_ip_lease_time_option(options, optlen,
762 					     &lease_time) == 0) {
763 		return lease_time;
764 	}
765 
766 	return CONFIG_NET_DHCPV4_SERVER_ADDR_LEASE_TIME;
767 }
768 
769 #if defined(DHCPV4_SERVER_ICMP_PROBE)
dhcpv4_probe_address(struct dhcpv4_server_ctx * ctx,struct dhcpv4_addr_slot * slot)770 static int dhcpv4_probe_address(struct dhcpv4_server_ctx *ctx,
771 				 struct dhcpv4_addr_slot *slot)
772 {
773 	struct sockaddr_in dest_addr = {
774 		.sin_family = AF_INET,
775 		.sin_addr = slot->addr,
776 	};
777 	int ret;
778 
779 	ret = net_icmp_send_echo_request(&ctx->probe_ctx.icmp_ctx, ctx->iface,
780 					 (struct sockaddr *)&dest_addr,
781 					 NULL, ctx);
782 	if (ret < 0) {
783 		LOG_ERR("Failed to send ICMP probe");
784 	}
785 
786 	return ret;
787 }
788 
echo_reply_handler(struct net_icmp_ctx * icmp_ctx,struct net_pkt * pkt,struct net_icmp_ip_hdr * ip_hdr,struct net_icmp_hdr * icmp_hdr,void * user_data)789 static int echo_reply_handler(struct net_icmp_ctx *icmp_ctx,
790 			      struct net_pkt *pkt,
791 			      struct net_icmp_ip_hdr *ip_hdr,
792 			      struct net_icmp_hdr *icmp_hdr,
793 			      void *user_data)
794 {
795 	struct dhcpv4_server_ctx *ctx = user_data;
796 	struct dhcpv4_server_probe_ctx *probe_ctx;
797 	struct dhcpv4_addr_slot *new_slot = NULL;
798 	struct in_addr peer_addr;
799 
800 	ARG_UNUSED(icmp_ctx);
801 	ARG_UNUSED(pkt);
802 	ARG_UNUSED(ip_hdr);
803 	ARG_UNUSED(icmp_hdr);
804 
805 	k_mutex_lock(&server_lock, K_FOREVER);
806 
807 	if (ctx == NULL) {
808 		goto out;
809 	}
810 
811 	probe_ctx = &ctx->probe_ctx;
812 
813 	if (probe_ctx->slot == NULL) {
814 		goto out;
815 	}
816 
817 	if (ip_hdr->family != AF_INET) {
818 		goto out;
819 	}
820 
821 	net_ipv4_addr_copy_raw((uint8_t *)&peer_addr, ip_hdr->ipv4->src);
822 	if (!net_ipv4_addr_cmp(&peer_addr, &probe_ctx->slot->addr)) {
823 		goto out;
824 	}
825 
826 	LOG_DBG("Got ICMP probe response, blocking address %s",
827 		net_sprint_ipv4_addr(&probe_ctx->slot->addr));
828 
829 	probe_ctx->slot->state = DHCPV4_SERVER_ADDR_DECLINED;
830 	probe_ctx->slot->expiry = sys_timepoint_calc(ADDRESS_DECLINED_TIMEOUT);
831 
832 	/* Try to find next free address */
833 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
834 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
835 
836 		if (slot->state == DHCPV4_SERVER_ADDR_FREE) {
837 			new_slot = slot;
838 			break;
839 		}
840 	}
841 
842 	if (new_slot == NULL) {
843 		LOG_DBG("No more free addresses to assign, ICMP probing stopped");
844 		probe_ctx->slot = NULL;
845 		dhcpv4_server_timeout_recalc(ctx);
846 		goto out;
847 	}
848 
849 	if (dhcpv4_probe_address(ctx, new_slot) < 0) {
850 		probe_ctx->slot = NULL;
851 		dhcpv4_server_timeout_recalc(ctx);
852 		goto out;
853 	}
854 
855 	new_slot->state = DHCPV4_SERVER_ADDR_RESERVED;
856 	new_slot->expiry = sys_timepoint_calc(ADDRESS_PROBE_TIMEOUT);
857 	new_slot->client_id.len = probe_ctx->slot->client_id.len;
858 	memcpy(new_slot->client_id.buf, probe_ctx->slot->client_id.buf,
859 	       new_slot->client_id.len);
860 	new_slot->lease_time = probe_ctx->slot->lease_time;
861 
862 	probe_ctx->slot = new_slot;
863 
864 	dhcpv4_server_timeout_recalc(ctx);
865 
866 out:
867 	k_mutex_unlock(&server_lock);
868 
869 	return 0;
870 }
871 
dhcpv4_server_probing_init(struct dhcpv4_server_ctx * ctx)872 static int dhcpv4_server_probing_init(struct dhcpv4_server_ctx *ctx)
873 {
874 	return net_icmp_init_ctx(&ctx->probe_ctx.icmp_ctx,
875 				 NET_ICMPV4_ECHO_REPLY, 0,
876 				 echo_reply_handler);
877 }
878 
dhcpv4_server_probing_deinit(struct dhcpv4_server_ctx * ctx)879 static void dhcpv4_server_probing_deinit(struct dhcpv4_server_ctx *ctx)
880 {
881 	(void)net_icmp_cleanup_ctx(&ctx->probe_ctx.icmp_ctx);
882 }
883 
dhcpv4_server_probe_setup(struct dhcpv4_server_ctx * ctx,struct dhcpv4_addr_slot * slot,struct dhcp_msg * msg,struct dhcpv4_parameter_request_list * params,struct dhcpv4_client_id * client_id)884 static int dhcpv4_server_probe_setup(struct dhcpv4_server_ctx *ctx,
885 				     struct dhcpv4_addr_slot *slot,
886 				     struct dhcp_msg *msg,
887 				     struct dhcpv4_parameter_request_list *params,
888 				     struct dhcpv4_client_id *client_id)
889 {
890 	int ret;
891 
892 	if (ctx->probe_ctx.slot != NULL) {
893 		return -EBUSY;
894 	}
895 
896 	ret = dhcpv4_probe_address(ctx, slot);
897 	if (ret < 0) {
898 		return ret;
899 	}
900 
901 	ctx->probe_ctx.slot = slot;
902 	ctx->probe_ctx.discovery = *msg;
903 	ctx->probe_ctx.params = *params;
904 	ctx->probe_ctx.client_id = *client_id;
905 
906 	return 0;
907 }
908 
dhcpv4_server_probe_timeout(struct dhcpv4_server_ctx * ctx,struct dhcpv4_addr_slot * slot)909 static void dhcpv4_server_probe_timeout(struct dhcpv4_server_ctx *ctx,
910 					struct dhcpv4_addr_slot *slot)
911 {
912 	/* Probe timer expired, send offer. */
913 	ctx->probe_ctx.slot = NULL;
914 
915 	(void)net_arp_clear_pending(ctx->iface, &slot->addr);
916 
917 	if (dhcpv4_send_offer(ctx, &ctx->probe_ctx.discovery, &slot->addr,
918 			      slot->lease_time, &ctx->probe_ctx.params,
919 			      &ctx->probe_ctx.client_id) < 0) {
920 		slot->state = DHCPV4_SERVER_ADDR_FREE;
921 		return;
922 	}
923 
924 	slot->expiry = sys_timepoint_calc(ADDRESS_RESERVED_TIMEOUT);
925 }
926 
dhcpv4_server_is_slot_probed(struct dhcpv4_server_ctx * ctx,struct dhcpv4_addr_slot * slot)927 static bool dhcpv4_server_is_slot_probed(struct dhcpv4_server_ctx *ctx,
928 					 struct dhcpv4_addr_slot *slot)
929 {
930 	return (ctx->probe_ctx.slot == slot);
931 }
932 #else /* defined(DHCPV4_SERVER_ICMP_PROBE) */
933 #define dhcpv4_server_probing_init(...) (0)
934 #define dhcpv4_server_probing_deinit(...)
935 #define dhcpv4_server_probe_setup(...) (-ENOTSUP)
936 #define dhcpv4_server_probe_timeout(...)
937 #define dhcpv4_server_is_slot_probed(...) (false)
938 #endif /* defined(DHCPV4_SERVER_ICMP_PROBE) */
939 
dhcpv4_handle_discover(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,uint8_t * options,uint8_t optlen)940 static void dhcpv4_handle_discover(struct dhcpv4_server_ctx *ctx,
941 				   struct dhcp_msg *msg, uint8_t *options,
942 				   uint8_t optlen)
943 {
944 	struct dhcpv4_parameter_request_list params = { 0 };
945 	struct dhcpv4_addr_slot *selected = NULL;
946 	struct dhcpv4_client_id client_id;
947 	bool probe = false;
948 	int ret;
949 
950 	ret = dhcpv4_get_client_id(msg, options, optlen, &client_id);
951 	if (ret < 0) {
952 		return;
953 	}
954 
955 	(void)dhcpv4_find_parameter_request_list_option(options, optlen, &params);
956 
957 	/* Address pool and address selection algorithm as
958 	 * described in 4.3.1
959 	 */
960 
961 	/* 1. Check for current bindings */
962 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
963 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
964 
965 		if ((slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
966 		     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED) &&
967 		    slot->client_id.len == client_id.len &&
968 		    memcmp(slot->client_id.buf, client_id.buf,
969 			    client_id.len) == 0) {
970 			if (slot->state == DHCPV4_SERVER_ADDR_RESERVED &&
971 			    dhcpv4_server_is_slot_probed(ctx, slot)) {
972 				LOG_DBG("ICMP probing in progress, ignore Discovery");
973 				return;
974 			}
975 
976 			/* Got match in current bindings. */
977 			selected = slot;
978 			break;
979 		}
980 		struct in_addr addr = { 0 };
981 
982 		if (slot->state == DHCPV4_SERVER_ADDR_FREE &&
983 		    address_provider_callback) {
984 			ret = address_provider_callback(ctx->iface, &client_id, &addr,
985 							address_provider_callback_user_data);
986 			if (ret == 0) {
987 				selected = slot;
988 				slot->addr = addr;
989 			}
990 			break;
991 		}
992 	}
993 
994 	/* 2. Skipped, for now expired/released entries are forgotten. */
995 
996 	/* 3. Check Requested IP Address option. */
997 	if (selected == NULL) {
998 		struct in_addr requested_ip;
999 
1000 		ret = dhcpv4_find_requested_ip_option(options, optlen,
1001 						      &requested_ip);
1002 		if (ret == 0) {
1003 			for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1004 				struct dhcpv4_addr_slot *slot =
1005 							&ctx->addr_pool[i];
1006 
1007 				if (net_ipv4_addr_cmp(&slot->addr,
1008 						      &requested_ip) &&
1009 				    slot->state == DHCPV4_SERVER_ADDR_FREE) {
1010 					/* Requested address is free. */
1011 					selected = slot;
1012 					probe = true;
1013 					break;
1014 				}
1015 			}
1016 		}
1017 	}
1018 
1019 	/* 4. Allocate new address from pool, if available. */
1020 	if (selected == NULL) {
1021 		struct in_addr giaddr;
1022 
1023 		memcpy(&giaddr, msg->giaddr, sizeof(giaddr));
1024 		if (!net_ipv4_is_addr_unspecified(&giaddr)) {
1025 			/* Only addresses in local subnet supported for now. */
1026 			return;
1027 		}
1028 
1029 		for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1030 			struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1031 
1032 			if (slot->state == DHCPV4_SERVER_ADDR_FREE) {
1033 				/* Requested address is free. */
1034 				selected = slot;
1035 				probe = true;
1036 				break;
1037 			}
1038 		}
1039 	}
1040 
1041 	/* In case no free address slot was found, as a last resort, try to
1042 	 * reuse the oldest declined entry, if present.
1043 	 */
1044 	if (selected == NULL) {
1045 		for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1046 			struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1047 
1048 			if (slot->state != DHCPV4_SERVER_ADDR_DECLINED) {
1049 				continue;
1050 			}
1051 
1052 			/* Find first to expire (oldest) entry. */
1053 			if ((selected == NULL) ||
1054 			    (sys_timepoint_cmp(slot->expiry,
1055 					       selected->expiry) < 0)) {
1056 				selected = slot;
1057 				probe = true;
1058 			}
1059 		}
1060 	}
1061 
1062 	if (selected == NULL) {
1063 		LOG_ERR("No free address found in address pool");
1064 	} else {
1065 		uint32_t lease_time = dhcpv4_get_lease_time(options, optlen);
1066 
1067 		if (IS_ENABLED(DHCPV4_SERVER_ICMP_PROBE) && probe) {
1068 			if (dhcpv4_server_probe_setup(ctx, selected, msg,
1069 						      &params, &client_id) < 0) {
1070 				/* Probing context already in use or failed to
1071 				 * send probe, ignore Discovery for now and wait
1072 				 * for retransmission.
1073 				 */
1074 				return;
1075 			}
1076 
1077 			selected->expiry =
1078 				sys_timepoint_calc(ADDRESS_PROBE_TIMEOUT);
1079 		} else {
1080 			if (dhcpv4_send_offer(ctx, msg, &selected->addr,
1081 					      lease_time, &params, &client_id) < 0) {
1082 				return;
1083 			}
1084 
1085 			selected->expiry =
1086 				sys_timepoint_calc(ADDRESS_RESERVED_TIMEOUT);
1087 		}
1088 
1089 		LOG_DBG("DHCPv4 processing Discover - reserved %s",
1090 			net_sprint_ipv4_addr(&selected->addr));
1091 
1092 		selected->state = DHCPV4_SERVER_ADDR_RESERVED;
1093 		selected->client_id.len = client_id.len;
1094 		memcpy(selected->client_id.buf, client_id.buf, client_id.len);
1095 		selected->lease_time = lease_time;
1096 		dhcpv4_server_timeout_recalc(ctx);
1097 	}
1098 }
1099 
dhcpv4_handle_request(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,uint8_t * options,uint8_t optlen)1100 static void dhcpv4_handle_request(struct dhcpv4_server_ctx *ctx,
1101 				  struct dhcp_msg *msg, uint8_t *options,
1102 				  uint8_t optlen)
1103 {
1104 	struct dhcpv4_parameter_request_list params = { 0 };
1105 	struct dhcpv4_addr_slot *selected = NULL;
1106 	struct dhcpv4_client_id client_id;
1107 	struct in_addr requested_ip, server_id, ciaddr, giaddr;
1108 	int ret;
1109 
1110 	memcpy(&ciaddr, msg->ciaddr, sizeof(ciaddr));
1111 	memcpy(&giaddr, msg->giaddr, sizeof(giaddr));
1112 
1113 	if (!net_ipv4_is_addr_unspecified(&giaddr)) {
1114 		/* Only addresses in local subnet supported for now. */
1115 		return;
1116 	}
1117 
1118 	ret = dhcpv4_get_client_id(msg, options, optlen, &client_id);
1119 	if (ret < 0) {
1120 		/* Failed to obtain Client ID, ignore. */
1121 		return;
1122 	}
1123 
1124 	(void)dhcpv4_find_parameter_request_list_option(options, optlen, &params);
1125 
1126 	ret = dhcpv4_find_server_id_option(options, optlen, &server_id);
1127 	if (ret == 0) {
1128 		/* Server ID present, Request generated during SELECTING. */
1129 		if (!net_ipv4_addr_cmp(&ctx->server_addr, &server_id)) {
1130 			/* Not for us, ignore. */
1131 			return;
1132 		}
1133 
1134 		ret = dhcpv4_find_requested_ip_option(options, optlen,
1135 						      &requested_ip);
1136 		if (ret < 0) {
1137 			/* Requested IP missing, ignore. */
1138 			return;
1139 		}
1140 
1141 		if (!net_ipv4_is_addr_unspecified(&ciaddr)) {
1142 			/* ciaddr MUST be zero */
1143 			return;
1144 		}
1145 
1146 		for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1147 			struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1148 
1149 			if (net_ipv4_addr_cmp(&slot->addr, &requested_ip) &&
1150 			    slot->client_id.len == client_id.len &&
1151 			    memcmp(slot->client_id.buf, client_id.buf,
1152 				   client_id.len) == 0 &&
1153 			    (slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
1154 			     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED)) {
1155 				selected = slot;
1156 				break;
1157 			}
1158 		}
1159 
1160 		if (selected == NULL) {
1161 			LOG_ERR("No valid slot found for DHCPv4 Request");
1162 		} else {
1163 			uint32_t lease_time = dhcpv4_get_lease_time(options, optlen);
1164 
1165 			if (dhcpv4_send_ack(ctx, msg, &selected->addr, lease_time,
1166 					    &params, &client_id, false) < 0) {
1167 				return;
1168 			}
1169 
1170 			LOG_DBG("DHCPv4 processing Request - allocated %s",
1171 				net_sprint_ipv4_addr(&selected->addr));
1172 
1173 			selected->lease_time = lease_time;
1174 			selected->expiry = sys_timepoint_calc(
1175 							K_SECONDS(lease_time));
1176 			selected->state = DHCPV4_SERVER_ADDR_ALLOCATED;
1177 			dhcpv4_server_timeout_recalc(ctx);
1178 		}
1179 
1180 		return;
1181 	}
1182 
1183 	/* No server ID option - check requested address. */
1184 	ret = dhcpv4_find_requested_ip_option(options, optlen, &requested_ip);
1185 	if (ret == 0) {
1186 		/* Requested IP present, Request generated during INIT-REBOOT. */
1187 		if (!net_ipv4_is_addr_unspecified(&ciaddr)) {
1188 			/* ciaddr MUST be zero */
1189 			return;
1190 		}
1191 
1192 		if (!net_if_ipv4_addr_mask_cmp(ctx->iface, &requested_ip)) {
1193 			/* Wrong subnet. */
1194 			dhcpv4_send_nak(ctx, msg, &client_id);
1195 		}
1196 
1197 		for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1198 			struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1199 
1200 			if (slot->client_id.len == client_id.len &&
1201 			    memcmp(slot->client_id.buf, client_id.buf,
1202 				   client_id.len) == 0 &&
1203 			    (slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
1204 			     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED)) {
1205 				selected = slot;
1206 				break;
1207 			}
1208 		}
1209 
1210 		if (selected != NULL) {
1211 			if (net_ipv4_addr_cmp(&selected->addr, &requested_ip)) {
1212 				uint32_t lease_time = dhcpv4_get_lease_time(
1213 								options, optlen);
1214 
1215 				if (dhcpv4_send_ack(ctx, msg, &selected->addr,
1216 						    lease_time, &params,
1217 						    &client_id, false) < 0) {
1218 					return;
1219 				}
1220 
1221 				selected->lease_time = lease_time;
1222 				selected->expiry = sys_timepoint_calc(
1223 							K_SECONDS(lease_time));
1224 				dhcpv4_server_timeout_recalc(ctx);
1225 			} else {
1226 				dhcpv4_send_nak(ctx, msg, &client_id);
1227 			}
1228 		} else if (IS_ENABLED(CONFIG_NET_DHCPV4_SERVER_NAK_UNRECOGNIZED_REQUESTS)) {
1229 			dhcpv4_send_nak(ctx, msg, &client_id);
1230 		}
1231 
1232 		/* No notion of the client, remain silent. */
1233 		return;
1234 	}
1235 
1236 	/* Neither server ID or requested IP set, Request generated during
1237 	 * RENEWING or REBINDING.
1238 	 */
1239 
1240 	if (!net_if_ipv4_addr_mask_cmp(ctx->iface, &ciaddr)) {
1241 		/* Wrong subnet. */
1242 		dhcpv4_send_nak(ctx, msg, &client_id);
1243 	}
1244 
1245 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1246 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1247 
1248 		if (net_ipv4_addr_cmp(&slot->addr, &ciaddr)) {
1249 			selected = slot;
1250 			break;
1251 		}
1252 	}
1253 
1254 	if (selected != NULL) {
1255 		if (selected->state == DHCPV4_SERVER_ADDR_ALLOCATED &&
1256 		    selected->client_id.len == client_id.len &&
1257 		    memcmp(selected->client_id.buf, client_id.buf,
1258 			   client_id.len) == 0) {
1259 			uint32_t lease_time = dhcpv4_get_lease_time(
1260 								options, optlen);
1261 
1262 			if (dhcpv4_send_ack(ctx, msg, &ciaddr, lease_time,
1263 					    &params, &client_id, false) < 0) {
1264 				return;
1265 			}
1266 
1267 			selected->lease_time = lease_time;
1268 			selected->expiry = sys_timepoint_calc(
1269 							K_SECONDS(lease_time));
1270 			dhcpv4_server_timeout_recalc(ctx);
1271 		} else {
1272 			dhcpv4_send_nak(ctx, msg, &client_id);
1273 		}
1274 	}
1275 }
1276 
dhcpv4_handle_decline(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,uint8_t * options,uint8_t optlen)1277 static void dhcpv4_handle_decline(struct dhcpv4_server_ctx *ctx,
1278 				  struct dhcp_msg *msg, uint8_t *options,
1279 				  uint8_t optlen)
1280 {
1281 	struct dhcpv4_client_id client_id;
1282 	struct in_addr requested_ip, server_id;
1283 	int ret;
1284 
1285 	ret = dhcpv4_find_server_id_option(options, optlen, &server_id);
1286 	if (ret < 0) {
1287 		/* No server ID, ignore. */
1288 		return;
1289 	}
1290 
1291 	if (!net_ipv4_addr_cmp(&ctx->server_addr, &server_id)) {
1292 		/* Not for us, ignore. */
1293 		return;
1294 	}
1295 
1296 	ret = dhcpv4_get_client_id(msg, options, optlen, &client_id);
1297 	if (ret < 0) {
1298 		/* Failed to obtain Client ID, ignore. */
1299 		return;
1300 	}
1301 
1302 	ret = dhcpv4_find_requested_ip_option(options, optlen,
1303 						&requested_ip);
1304 	if (ret < 0) {
1305 		/* Requested IP missing, ignore. */
1306 		return;
1307 	}
1308 
1309 	LOG_ERR("Received DHCPv4 Decline for %s (address already in use)",
1310 		net_sprint_ipv4_addr(&requested_ip));
1311 
1312 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1313 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1314 
1315 		if (net_ipv4_addr_cmp(&slot->addr, &requested_ip) &&
1316 		    slot->client_id.len == client_id.len &&
1317 		    memcmp(slot->client_id.buf, client_id.buf,
1318 				client_id.len) == 0 &&
1319 		    (slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
1320 		     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED)) {
1321 			slot->state = DHCPV4_SERVER_ADDR_DECLINED;
1322 			slot->expiry =
1323 				sys_timepoint_calc(ADDRESS_DECLINED_TIMEOUT);
1324 			dhcpv4_server_timeout_recalc(ctx);
1325 			break;
1326 		}
1327 	}
1328 }
1329 
dhcpv4_handle_release(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,uint8_t * options,uint8_t optlen)1330 static void dhcpv4_handle_release(struct dhcpv4_server_ctx *ctx,
1331 				  struct dhcp_msg *msg, uint8_t *options,
1332 				  uint8_t optlen)
1333 {
1334 	struct dhcpv4_client_id client_id;
1335 	struct in_addr ciaddr, server_id;
1336 	int ret;
1337 
1338 	ret = dhcpv4_find_server_id_option(options, optlen, &server_id);
1339 	if (ret < 0) {
1340 		/* No server ID, ignore. */
1341 		return;
1342 	}
1343 
1344 	if (!net_ipv4_addr_cmp(&ctx->server_addr, &server_id)) {
1345 		/* Not for us, ignore. */
1346 		return;
1347 	}
1348 
1349 	ret = dhcpv4_get_client_id(msg, options, optlen, &client_id);
1350 	if (ret < 0) {
1351 		/* Failed to obtain Client ID, ignore. */
1352 		return;
1353 	}
1354 
1355 	memcpy(&ciaddr, msg->ciaddr, sizeof(ciaddr));
1356 
1357 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1358 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1359 
1360 		if (net_ipv4_addr_cmp(&slot->addr, &ciaddr) &&
1361 		    slot->client_id.len == client_id.len &&
1362 		    memcmp(slot->client_id.buf, client_id.buf,
1363 				client_id.len) == 0 &&
1364 		    (slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
1365 		     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED)) {
1366 			LOG_DBG("DHCPv4 processing Release - %s",
1367 				net_sprint_ipv4_addr(&slot->addr));
1368 
1369 			slot->state = DHCPV4_SERVER_ADDR_FREE;
1370 			slot->expiry = sys_timepoint_calc(K_FOREVER);
1371 			dhcpv4_server_timeout_recalc(ctx);
1372 			break;
1373 		}
1374 	}
1375 }
1376 
dhcpv4_handle_inform(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,uint8_t * options,uint8_t optlen)1377 static void dhcpv4_handle_inform(struct dhcpv4_server_ctx *ctx,
1378 				 struct dhcp_msg *msg, uint8_t *options,
1379 				 uint8_t optlen)
1380 {
1381 	struct dhcpv4_parameter_request_list params = { 0 };
1382 
1383 	(void)dhcpv4_find_parameter_request_list_option(options, optlen, &params);
1384 	(void)dhcpv4_send_ack(ctx, msg, (struct in_addr *)msg->ciaddr, 0,
1385 			      &params, NULL, true);
1386 }
1387 
1388 /* Server core. */
1389 
dhcpv4_server_timeout(struct k_work * work)1390 static void dhcpv4_server_timeout(struct k_work *work)
1391 {
1392 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
1393 	struct dhcpv4_server_ctx *ctx =
1394 		CONTAINER_OF(dwork, struct dhcpv4_server_ctx, timeout_work);
1395 
1396 	k_mutex_lock(&server_lock, K_FOREVER);
1397 
1398 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1399 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1400 
1401 		if ((slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
1402 		     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED) &&
1403 		     sys_timepoint_expired(slot->expiry)) {
1404 			if (slot->state == DHCPV4_SERVER_ADDR_RESERVED &&
1405 			    dhcpv4_server_is_slot_probed(ctx, slot)) {
1406 				dhcpv4_server_probe_timeout(ctx, slot);
1407 			} else {
1408 				LOG_DBG("Address %s expired",
1409 					net_sprint_ipv4_addr(&slot->addr));
1410 				slot->state = DHCPV4_SERVER_ADDR_FREE;
1411 			}
1412 		}
1413 
1414 		if (slot->state == DHCPV4_SERVER_ADDR_DECLINED &&
1415 		    sys_timepoint_expired(slot->expiry)) {
1416 			slot->state = DHCPV4_SERVER_ADDR_FREE;
1417 		}
1418 	}
1419 
1420 	dhcpv4_server_timeout_recalc(ctx);
1421 
1422 	k_mutex_unlock(&server_lock);
1423 }
1424 
dhcpv4_process_data(struct dhcpv4_server_ctx * ctx,uint8_t * data,size_t datalen)1425 static void dhcpv4_process_data(struct dhcpv4_server_ctx *ctx, uint8_t *data,
1426 				size_t datalen)
1427 {
1428 	struct dhcp_msg *msg;
1429 	uint8_t msgtype;
1430 	int ret;
1431 
1432 	if (datalen < sizeof(struct dhcp_msg)) {
1433 		LOG_DBG("DHCPv4 server malformed message");
1434 		return;
1435 	}
1436 
1437 	msg = (struct dhcp_msg *)data;
1438 
1439 	if (msg->op != DHCPV4_MSG_BOOT_REQUEST) {
1440 		/* Silently drop messages other than BOOTREQUEST */
1441 		return;
1442 	}
1443 
1444 	data += sizeof(struct dhcp_msg);
1445 	datalen -= sizeof(struct dhcp_msg);
1446 
1447 	/* Skip server hostname/filename/option cookie */
1448 	if (datalen < (SIZE_OF_SNAME + SIZE_OF_FILE + SIZE_OF_MAGIC_COOKIE)) {
1449 		return;
1450 	}
1451 
1452 	data += SIZE_OF_SNAME + SIZE_OF_FILE + SIZE_OF_MAGIC_COOKIE;
1453 	datalen -= SIZE_OF_SNAME + SIZE_OF_FILE + SIZE_OF_MAGIC_COOKIE;
1454 
1455 	/* Search options for DHCP message type. */
1456 	ret = dhcpv4_find_message_type_option(data, datalen, &msgtype);
1457 	if (ret < 0) {
1458 		LOG_ERR("No message type option");
1459 		return;
1460 	}
1461 
1462 	k_mutex_lock(&server_lock, K_FOREVER);
1463 
1464 	switch (msgtype) {
1465 	case NET_DHCPV4_MSG_TYPE_DISCOVER:
1466 		dhcpv4_handle_discover(ctx, msg, data, datalen);
1467 		break;
1468 	case NET_DHCPV4_MSG_TYPE_REQUEST:
1469 		dhcpv4_handle_request(ctx, msg, data, datalen);
1470 		break;
1471 	case NET_DHCPV4_MSG_TYPE_DECLINE:
1472 		dhcpv4_handle_decline(ctx, msg, data, datalen);
1473 		break;
1474 	case NET_DHCPV4_MSG_TYPE_RELEASE:
1475 		dhcpv4_handle_release(ctx, msg, data, datalen);
1476 		break;
1477 	case NET_DHCPV4_MSG_TYPE_INFORM:
1478 		dhcpv4_handle_inform(ctx, msg, data, datalen);
1479 		break;
1480 
1481 	case NET_DHCPV4_MSG_TYPE_OFFER:
1482 	case NET_DHCPV4_MSG_TYPE_ACK:
1483 	case NET_DHCPV4_MSG_TYPE_NAK:
1484 	default:
1485 		/* Ignore server initiated and unknown message types. */
1486 		break;
1487 	}
1488 
1489 	k_mutex_unlock(&server_lock);
1490 }
1491 
dhcpv4_server_cb(struct net_socket_service_event * evt)1492 static void dhcpv4_server_cb(struct net_socket_service_event *evt)
1493 {
1494 	struct dhcpv4_server_ctx *ctx = NULL;
1495 	uint8_t recv_buf[NET_IPV4_MTU];
1496 	int ret;
1497 
1498 	for (int i = 0; i < ARRAY_SIZE(server_ctx); i++) {
1499 		if (server_ctx[i].sock == evt->event.fd) {
1500 			ctx = &server_ctx[i];
1501 			break;
1502 		}
1503 	}
1504 
1505 	if (ctx == NULL) {
1506 		LOG_ERR("No DHCPv4 server context found for given FD.");
1507 		return;
1508 	}
1509 
1510 	if (evt->event.revents & ZSOCK_POLLERR) {
1511 		LOG_ERR("DHCPv4 server poll revents error");
1512 		net_dhcpv4_server_stop(ctx->iface);
1513 		return;
1514 	}
1515 
1516 	if (!(evt->event.revents & ZSOCK_POLLIN)) {
1517 		return;
1518 	}
1519 
1520 	ret = zsock_recvfrom(evt->event.fd, recv_buf, sizeof(recv_buf),
1521 			     ZSOCK_MSG_DONTWAIT, NULL, 0);
1522 	if (ret < 0) {
1523 		if (errno == EAGAIN) {
1524 			return;
1525 		}
1526 
1527 		LOG_ERR("DHCPv4 server recv error, %d", errno);
1528 		net_dhcpv4_server_stop(ctx->iface);
1529 		return;
1530 	}
1531 
1532 	dhcpv4_process_data(ctx, recv_buf, ret);
1533 }
1534 
1535 NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(dhcpv4_server, dhcpv4_server_cb,
1536 				      CONFIG_NET_DHCPV4_SERVER_INSTANCES);
1537 
net_dhcpv4_server_start(struct net_if * iface,struct in_addr * base_addr)1538 int net_dhcpv4_server_start(struct net_if *iface, struct in_addr *base_addr)
1539 {
1540 	struct sockaddr_in addr = {
1541 		.sin_family = AF_INET,
1542 		.sin_addr = INADDR_ANY_INIT,
1543 		.sin_port = htons(DHCPV4_SERVER_PORT),
1544 	};
1545 	struct ifreq ifreq = { 0 };
1546 	int ret, sock = -1, slot = -1;
1547 	const struct in_addr *server_addr;
1548 	struct in_addr netmask;
1549 
1550 	if (iface == NULL || base_addr == NULL) {
1551 		return -EINVAL;
1552 	}
1553 
1554 	if (!net_if_ipv4_addr_mask_cmp(iface, base_addr)) {
1555 		LOG_ERR("Address pool does not belong to the interface subnet.");
1556 		return -EINVAL;
1557 	}
1558 
1559 	server_addr = net_if_ipv4_select_src_addr(iface, base_addr);
1560 	if (server_addr == NULL) {
1561 		LOG_ERR("Failed to obtain a valid server address.");
1562 		return -EINVAL;
1563 	}
1564 
1565 	if ((htonl(server_addr->s_addr) >= htonl(base_addr->s_addr)) &&
1566 	    (htonl(server_addr->s_addr) <
1567 	     htonl(base_addr->s_addr) + CONFIG_NET_DHCPV4_SERVER_ADDR_COUNT)) {
1568 		LOG_ERR("Address pool overlaps with server address.");
1569 		return -EINVAL;
1570 	}
1571 
1572 	netmask = net_if_ipv4_get_netmask_by_addr(iface, server_addr);
1573 	if (net_ipv4_is_addr_unspecified(&netmask)) {
1574 		LOG_ERR("Failed to obtain subnet mask.");
1575 		return -EINVAL;
1576 	}
1577 
1578 	k_mutex_lock(&server_lock, K_FOREVER);
1579 
1580 	for (int i = 0; i < ARRAY_SIZE(server_ctx); i++) {
1581 		if (server_ctx[i].iface != NULL) {
1582 			if (server_ctx[i].iface == iface) {
1583 				LOG_ERR("DHCPv4 server instance already running.");
1584 				ret = -EALREADY;
1585 				goto error;
1586 			}
1587 		} else {
1588 			if (slot < 0) {
1589 				slot = i;
1590 			}
1591 		}
1592 	}
1593 
1594 	if (slot < 0) {
1595 		LOG_ERR("No free DHCPv4 server instance.");
1596 		ret = -ENOMEM;
1597 		goto error;
1598 	}
1599 
1600 	ret = net_if_get_name(iface, ifreq.ifr_name, sizeof(ifreq.ifr_name));
1601 	if (ret < 0) {
1602 		LOG_ERR("Failed to obtain interface name.");
1603 		goto error;
1604 	}
1605 
1606 	sock = zsock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1607 	if (sock < 0) {
1608 		ret = -errno;
1609 		LOG_ERR("Failed to create DHCPv4 server socket, %d", ret);
1610 		goto error;
1611 	}
1612 
1613 	ret = zsock_setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq,
1614 			       sizeof(ifreq));
1615 	if (ret < 0) {
1616 		ret = -errno;
1617 		LOG_ERR("Failed to bind DHCPv4 server socket with interface, %d",
1618 			ret);
1619 		goto error;
1620 	}
1621 
1622 	ret = zsock_bind(sock, (struct sockaddr *)&addr, sizeof(addr));
1623 	if (ret < 0) {
1624 		ret = -errno;
1625 		LOG_ERR("Failed to bind DHCPv4 server socket, %d", ret);
1626 		goto error;
1627 	}
1628 
1629 	fds[slot].fd = sock;
1630 	fds[slot].events = ZSOCK_POLLIN;
1631 
1632 	server_ctx[slot].iface = iface;
1633 	server_ctx[slot].sock = sock;
1634 	server_ctx[slot].server_addr = *server_addr;
1635 	server_ctx[slot].netmask = netmask;
1636 
1637 	k_work_init_delayable(&server_ctx[slot].timeout_work,
1638 			      dhcpv4_server_timeout);
1639 
1640 	LOG_DBG("Started DHCPv4 server, address pool:");
1641 	for (int i = 0; i < ARRAY_SIZE(server_ctx[slot].addr_pool); i++) {
1642 		server_ctx[slot].addr_pool[i].state = DHCPV4_SERVER_ADDR_FREE;
1643 		server_ctx[slot].addr_pool[i].addr.s_addr =
1644 					htonl(ntohl(base_addr->s_addr) + i);
1645 
1646 		LOG_DBG("\t%2d: %s", i,
1647 			net_sprint_ipv4_addr(
1648 				&server_ctx[slot].addr_pool[i].addr));
1649 	}
1650 
1651 	ret = dhcpv4_server_probing_init(&server_ctx[slot]);
1652 	if (ret < 0) {
1653 		LOG_ERR("Failed to register probe handler, %d", ret);
1654 		goto cleanup;
1655 	}
1656 
1657 	ret = net_socket_service_register(&dhcpv4_server, fds, ARRAY_SIZE(fds),
1658 					  NULL);
1659 	if (ret < 0) {
1660 		LOG_ERR("Failed to register socket service, %d", ret);
1661 		dhcpv4_server_probing_deinit(&server_ctx[slot]);
1662 		goto cleanup;
1663 	}
1664 
1665 	k_mutex_unlock(&server_lock);
1666 
1667 	return 0;
1668 
1669 cleanup:
1670 	memset(&server_ctx[slot], 0, sizeof(server_ctx[slot]));
1671 	fds[slot].fd = -1;
1672 
1673 error:
1674 	if (sock >= 0) {
1675 		(void)zsock_close(sock);
1676 	}
1677 
1678 	k_mutex_unlock(&server_lock);
1679 
1680 	return ret;
1681 }
1682 
net_dhcpv4_server_stop(struct net_if * iface)1683 int net_dhcpv4_server_stop(struct net_if *iface)
1684 {
1685 	struct k_work_sync sync;
1686 	int slot = -1;
1687 	int ret = 0;
1688 	bool service_stop = true;
1689 
1690 	if (iface == NULL) {
1691 		return -EINVAL;
1692 	}
1693 
1694 	k_mutex_lock(&server_lock, K_FOREVER);
1695 
1696 	for (int i = 0; i < ARRAY_SIZE(server_ctx); i++) {
1697 		if (server_ctx[i].iface == iface) {
1698 			slot = i;
1699 			break;
1700 		}
1701 	}
1702 
1703 	if (slot < 0) {
1704 		ret = -ENOENT;
1705 		goto out;
1706 	}
1707 
1708 	fds[slot].fd = -1;
1709 	(void)zsock_close(server_ctx[slot].sock);
1710 
1711 	dhcpv4_server_probing_deinit(&server_ctx[slot]);
1712 	k_work_cancel_delayable_sync(&server_ctx[slot].timeout_work, &sync);
1713 
1714 	memset(&server_ctx[slot], 0, sizeof(server_ctx[slot]));
1715 
1716 	for (int i = 0; i < ARRAY_SIZE(fds); i++) {
1717 		if (fds[i].fd >= 0) {
1718 			service_stop = false;
1719 			break;
1720 		}
1721 	}
1722 
1723 	if (service_stop) {
1724 		ret = net_socket_service_unregister(&dhcpv4_server);
1725 	} else {
1726 		ret = net_socket_service_register(&dhcpv4_server, fds,
1727 						  ARRAY_SIZE(fds), NULL);
1728 	}
1729 
1730 out:
1731 	k_mutex_unlock(&server_lock);
1732 
1733 	return ret;
1734 }
1735 
dhcpv4_server_foreach_lease_on_ctx(struct dhcpv4_server_ctx * ctx,net_dhcpv4_lease_cb_t cb,void * user_data)1736 static void dhcpv4_server_foreach_lease_on_ctx(struct dhcpv4_server_ctx *ctx,
1737 						 net_dhcpv4_lease_cb_t cb,
1738 						 void *user_data)
1739 {
1740 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1741 		struct dhcpv4_addr_slot *addr = &ctx->addr_pool[i];
1742 
1743 		if (addr->state != DHCPV4_SERVER_ADDR_FREE) {
1744 			cb(ctx->iface, addr, user_data);
1745 		}
1746 	}
1747 }
1748 
net_dhcpv4_server_foreach_lease(struct net_if * iface,net_dhcpv4_lease_cb_t cb,void * user_data)1749 int net_dhcpv4_server_foreach_lease(struct net_if *iface,
1750 				    net_dhcpv4_lease_cb_t cb,
1751 				    void *user_data)
1752 {
1753 	int slot = -1;
1754 	int ret = 0;
1755 
1756 	k_mutex_lock(&server_lock, K_FOREVER);
1757 
1758 	if (iface == NULL) {
1759 		for (int i = 0; i < ARRAY_SIZE(server_ctx); i++) {
1760 			if (server_ctx[i].iface != NULL) {
1761 				dhcpv4_server_foreach_lease_on_ctx(
1762 						&server_ctx[i], cb, user_data);
1763 			}
1764 		}
1765 
1766 		return 0;
1767 	}
1768 
1769 	for (int i = 0; i < ARRAY_SIZE(server_ctx); i++) {
1770 		if (server_ctx[i].iface == iface) {
1771 			slot = i;
1772 			break;
1773 		}
1774 	}
1775 
1776 	if (slot < 0) {
1777 		ret = -ENOENT;
1778 		goto out;
1779 	}
1780 
1781 	dhcpv4_server_foreach_lease_on_ctx(&server_ctx[slot], cb, user_data);
1782 
1783 out:
1784 	k_mutex_unlock(&server_lock);
1785 
1786 	return ret;
1787 }
1788 
net_dhcpv4_server_set_provider_cb(net_dhcpv4_server_provider_cb_t cb,void * user_data)1789 void net_dhcpv4_server_set_provider_cb(net_dhcpv4_server_provider_cb_t cb, void *user_data)
1790 {
1791 	address_provider_callback_user_data = user_data;
1792 	address_provider_callback = cb;
1793 }
1794 
net_dhcpv4_server_init(void)1795 void net_dhcpv4_server_init(void)
1796 {
1797 	address_provider_callback = NULL;
1798 	address_provider_callback_user_data = NULL;
1799 
1800 	for (int i = 0; i < ARRAY_SIZE(fds); i++) {
1801 		fds[i].fd = -1;
1802 	}
1803 }
1804