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 			if (!IS_ENABLED(CONFIG_NET_DHCPV4_SERVER_OPTION_ROUTER)) {
520 				break;
521 			}
522 
523 			buf = dhcpv4_encode_router_option(
524 				buf, buflen, &ctx->iface->config.ip.ipv4->gw);
525 			if (buf == NULL) {
526 				goto out;
527 			}
528 			break;
529 
530 		case DHCPV4_OPTIONS_DNS_SERVER:
531 			if (strlen(CONFIG_NET_DHCPV4_SERVER_OPTION_DNS_ADDRESS) == 0) {
532 				break;
533 			}
534 
535 			buf = dhcpv4_encode_dns_server_option(buf, buflen);
536 			if (buf == NULL) {
537 				goto out;
538 			}
539 			break;
540 		/* Others - just ignore. */
541 		default:
542 			break;
543 		}
544 	}
545 
546 out:
547 	return buf;
548 }
549 
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)550 static int dhcpv4_send(struct dhcpv4_server_ctx *ctx, enum net_dhcpv4_msg_type type,
551 		       uint8_t *reply, size_t len, struct dhcp_msg *msg,
552 		       struct in_addr *yiaddr)
553 {
554 	struct sockaddr_in dst_addr = {
555 		.sin_family = AF_INET,
556 		.sin_port = htons(DHCPV4_CLIENT_PORT),
557 	};
558 	struct in_addr giaddr; /* Relay agent address */
559 	struct in_addr ciaddr; /* Client address */
560 	int ret;
561 
562 	memcpy(&giaddr, msg->giaddr, sizeof(giaddr));
563 	memcpy(&ciaddr, msg->ciaddr, sizeof(ciaddr));
564 
565 	/* Select destination address as described in ch. 4.1. */
566 	if (!net_ipv4_is_addr_unspecified(&giaddr)) {
567 		/* If the 'giaddr' field in a DHCP message from a client is
568 		 * non-zero, the server sends any return messages to the
569 		 * 'DHCP server' port on the BOOTP relay agent whose address
570 		 * appears in 'giaddr'.
571 		 */
572 		dst_addr.sin_addr = giaddr;
573 		dst_addr.sin_port = htons(DHCPV4_SERVER_PORT);
574 	} else if (type == NET_DHCPV4_MSG_TYPE_NAK) {
575 		/* In all cases, when 'giaddr' is zero, the server broadcasts
576 		 * any DHCPNAK messages to 0xffffffff.
577 		 */
578 		dst_addr.sin_addr = *net_ipv4_broadcast_address();
579 	} else if (!net_ipv4_is_addr_unspecified(&ciaddr)) {
580 		/* If the 'giaddr' field is zero and the 'ciaddr' field is
581 		 * nonzero, then the server unicasts DHCPOFFER and DHCPACK
582 		 * messages to the address in 'ciaddr'.
583 		 */
584 		dst_addr.sin_addr = ciaddr;
585 	} else if (ntohs(msg->flags) & DHCPV4_MSG_BROADCAST) {
586 		/* If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast
587 		 * bit is set, then the server broadcasts DHCPOFFER and DHCPACK
588 		 * messages to 0xffffffff.
589 		 */
590 		dst_addr.sin_addr = *net_ipv4_broadcast_address();
591 	} else if (yiaddr != NULL) {
592 		/* If the broadcast bit is not set and 'giaddr' is zero and
593 		 * 'ciaddr' is zero, then the server unicasts DHCPOFFER and
594 		 * DHCPACK messages to the client's hardware address and 'yiaddr'
595 		 * address.
596 		 */
597 		struct net_eth_addr hwaddr;
598 
599 		memcpy(&hwaddr, msg->chaddr, sizeof(hwaddr));
600 		net_arp_update(ctx->iface, yiaddr, &hwaddr, false, true);
601 		dst_addr.sin_addr = *yiaddr;
602 	} else {
603 		NET_ERR("Unspecified destination address.");
604 		return -EDESTADDRREQ;
605 	}
606 
607 	ret = zsock_sendto(ctx->sock, reply, len, 0, (struct sockaddr *)&dst_addr,
608 			   sizeof(dst_addr));
609 	if (ret < 0) {
610 		return -errno;
611 	}
612 
613 	return 0;
614 }
615 
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)616 static int dhcpv4_send_offer(struct dhcpv4_server_ctx *ctx, struct dhcp_msg *msg,
617 			     struct in_addr *addr, uint32_t lease_time,
618 			     struct dhcpv4_parameter_request_list *params,
619 			     struct dhcpv4_client_id *client_id)
620 {
621 	uint8_t reply[NET_IPV4_MTU];
622 	uint8_t *buf = reply;
623 	size_t buflen = sizeof(reply);
624 	size_t reply_len = 0;
625 	int ret;
626 
627 	buf = dhcpv4_encode_header(buf, &buflen, msg, addr);
628 	buf = dhcpv4_encode_sname(buf, &buflen, NULL);
629 	buf = dhcpv4_encode_file(buf, &buflen, NULL);
630 	buf = dhcpv4_encode_magic_cookie(buf, &buflen);
631 	buf = dhcpv4_encode_ip_lease_time_option(buf, &buflen, lease_time);
632 	buf = dhcpv4_encode_message_type_option(buf, &buflen,
633 						NET_DHCPV4_MSG_TYPE_OFFER);
634 	buf = dhcpv4_encode_server_id_option(buf, &buflen, &ctx->server_addr);
635 	buf = dhcpv4_encode_client_id_option(buf, &buflen, client_id);
636 	buf = dhcpv4_encode_requested_params(buf, &buflen, ctx, params);
637 	buf = dhcpv4_encode_end_option(buf, &buflen);
638 
639 	if (buf == NULL) {
640 		LOG_ERR("Failed to encode %s message", "Offer");
641 		return -ENOMEM;
642 	}
643 
644 	reply_len = sizeof(reply) - buflen;
645 
646 	ret = dhcpv4_send(ctx, NET_DHCPV4_MSG_TYPE_OFFER, reply, reply_len,
647 			  msg, addr);
648 	if (ret < 0) {
649 		LOG_ERR("Failed to send %s message, %d", "Offer", ret);
650 		return ret;
651 	}
652 
653 	return 0;
654 }
655 
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)656 static int dhcpv4_send_ack(struct dhcpv4_server_ctx *ctx, struct dhcp_msg *msg,
657 			   struct in_addr *addr, uint32_t lease_time,
658 			   struct dhcpv4_parameter_request_list *params,
659 			   struct dhcpv4_client_id *client_id,
660 			   bool inform)
661 {
662 	uint8_t reply[NET_IPV4_MTU];
663 	uint8_t *buf = reply;
664 	size_t buflen = sizeof(reply);
665 	size_t reply_len = 0;
666 	int ret;
667 
668 	buf = dhcpv4_encode_header(buf, &buflen, msg, inform ? NULL : addr);
669 	buf = dhcpv4_encode_sname(buf, &buflen, NULL);
670 	buf = dhcpv4_encode_file(buf, &buflen, NULL);
671 	buf = dhcpv4_encode_magic_cookie(buf, &buflen);
672 	if (!inform) {
673 		buf = dhcpv4_encode_ip_lease_time_option(buf, &buflen, lease_time);
674 	}
675 	buf = dhcpv4_encode_message_type_option(buf, &buflen,
676 						NET_DHCPV4_MSG_TYPE_ACK);
677 	buf = dhcpv4_encode_server_id_option(buf, &buflen, &ctx->server_addr);
678 	if (!inform) {
679 		buf = dhcpv4_encode_client_id_option(buf, &buflen, client_id);
680 	}
681 	buf = dhcpv4_encode_requested_params(buf, &buflen, ctx, params);
682 	buf = dhcpv4_encode_end_option(buf, &buflen);
683 
684 	if (buf == NULL) {
685 		LOG_ERR("Failed to encode %s message", "ACK");
686 		return -ENOMEM;
687 	}
688 
689 	reply_len = sizeof(reply) - buflen;
690 
691 	ret = dhcpv4_send(ctx, NET_DHCPV4_MSG_TYPE_ACK, reply, reply_len, msg,
692 			  addr);
693 	if (ret < 0) {
694 		LOG_ERR("Failed to send %s message, %d", "ACK", ret);
695 		return ret;
696 	}
697 
698 	return 0;
699 }
700 
dhcpv4_send_nak(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,struct dhcpv4_client_id * client_id)701 static int dhcpv4_send_nak(struct dhcpv4_server_ctx *ctx, struct dhcp_msg *msg,
702 			   struct dhcpv4_client_id *client_id)
703 {
704 	uint8_t reply[NET_IPV4_MTU];
705 	uint8_t *buf = reply;
706 	size_t buflen = sizeof(reply);
707 	size_t reply_len = 0;
708 	int ret;
709 
710 	buf = dhcpv4_encode_header(buf, &buflen, msg, NULL);
711 	buf = dhcpv4_encode_sname(buf, &buflen, NULL);
712 	buf = dhcpv4_encode_file(buf, &buflen, NULL);
713 	buf = dhcpv4_encode_magic_cookie(buf, &buflen);
714 	buf = dhcpv4_encode_message_type_option(buf, &buflen,
715 						NET_DHCPV4_MSG_TYPE_NAK);
716 	buf = dhcpv4_encode_server_id_option(buf, &buflen, &ctx->server_addr);
717 	buf = dhcpv4_encode_client_id_option(buf, &buflen, client_id);
718 	buf = dhcpv4_encode_end_option(buf, &buflen);
719 
720 	if (buf == NULL) {
721 		LOG_ERR("Failed to encode %s message", "NAK");
722 		return -ENOMEM;
723 	}
724 
725 	reply_len = sizeof(reply) - buflen;
726 
727 	ret = dhcpv4_send(ctx, NET_DHCPV4_MSG_TYPE_NAK, reply, reply_len, msg,
728 			  NULL);
729 	if (ret < 0) {
730 		LOG_ERR("Failed to send %s message, %d", "NAK", ret);
731 		return ret;
732 	}
733 
734 	return 0;
735 }
736 
737 /* Message handlers. */
738 
dhcpv4_get_client_id(struct dhcp_msg * msg,uint8_t * options,uint8_t optlen,struct dhcpv4_client_id * client_id)739 static int dhcpv4_get_client_id(struct dhcp_msg *msg, uint8_t *options,
740 				uint8_t optlen, struct dhcpv4_client_id *client_id)
741 {
742 	int ret;
743 
744 	client_id->len = sizeof(client_id->buf);
745 
746 	ret = dhcpv4_find_client_id_option(options, optlen, client_id->buf,
747 					   &client_id->len);
748 	if (ret == 0) {
749 		return 0;
750 	}
751 
752 	/* No Client Id option or too long to use, fallback to hardware address. */
753 	if (msg->hlen > sizeof(msg->chaddr)) {
754 		LOG_ERR("Malformed chaddr length.");
755 		return -EINVAL;
756 	}
757 
758 	client_id->buf[0] = msg->htype;
759 	memcpy(client_id->buf + 1, msg->chaddr, msg->hlen);
760 	client_id->len = msg->hlen + 1;
761 
762 	return 0;
763 }
764 
dhcpv4_get_lease_time(uint8_t * options,uint8_t optlen)765 static uint32_t dhcpv4_get_lease_time(uint8_t *options, uint8_t optlen)
766 {
767 	uint32_t lease_time;
768 
769 	if (dhcpv4_find_ip_lease_time_option(options, optlen,
770 					     &lease_time) == 0) {
771 		return lease_time;
772 	}
773 
774 	return CONFIG_NET_DHCPV4_SERVER_ADDR_LEASE_TIME;
775 }
776 
777 #if defined(DHCPV4_SERVER_ICMP_PROBE)
dhcpv4_probe_address(struct dhcpv4_server_ctx * ctx,struct dhcpv4_addr_slot * slot)778 static int dhcpv4_probe_address(struct dhcpv4_server_ctx *ctx,
779 				 struct dhcpv4_addr_slot *slot)
780 {
781 	struct sockaddr_in dest_addr = {
782 		.sin_family = AF_INET,
783 		.sin_addr = slot->addr,
784 	};
785 	int ret;
786 
787 	ret = net_icmp_send_echo_request(&ctx->probe_ctx.icmp_ctx, ctx->iface,
788 					 (struct sockaddr *)&dest_addr,
789 					 NULL, ctx);
790 	if (ret < 0) {
791 		LOG_ERR("Failed to send ICMP probe");
792 	}
793 
794 	return ret;
795 }
796 
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)797 static int echo_reply_handler(struct net_icmp_ctx *icmp_ctx,
798 			      struct net_pkt *pkt,
799 			      struct net_icmp_ip_hdr *ip_hdr,
800 			      struct net_icmp_hdr *icmp_hdr,
801 			      void *user_data)
802 {
803 	struct dhcpv4_server_ctx *ctx = user_data;
804 	struct dhcpv4_server_probe_ctx *probe_ctx;
805 	struct dhcpv4_addr_slot *new_slot = NULL;
806 	struct in_addr peer_addr;
807 
808 	ARG_UNUSED(icmp_ctx);
809 	ARG_UNUSED(pkt);
810 	ARG_UNUSED(ip_hdr);
811 	ARG_UNUSED(icmp_hdr);
812 
813 	k_mutex_lock(&server_lock, K_FOREVER);
814 
815 	if (ctx == NULL) {
816 		goto out;
817 	}
818 
819 	probe_ctx = &ctx->probe_ctx;
820 
821 	if (probe_ctx->slot == NULL) {
822 		goto out;
823 	}
824 
825 	if (ip_hdr->family != AF_INET) {
826 		goto out;
827 	}
828 
829 	net_ipv4_addr_copy_raw((uint8_t *)&peer_addr, ip_hdr->ipv4->src);
830 	if (!net_ipv4_addr_cmp(&peer_addr, &probe_ctx->slot->addr)) {
831 		goto out;
832 	}
833 
834 	LOG_DBG("Got ICMP probe response, blocking address %s",
835 		net_sprint_ipv4_addr(&probe_ctx->slot->addr));
836 
837 	probe_ctx->slot->state = DHCPV4_SERVER_ADDR_DECLINED;
838 	probe_ctx->slot->expiry = sys_timepoint_calc(ADDRESS_DECLINED_TIMEOUT);
839 
840 	/* Try to find next free address */
841 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
842 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
843 
844 		if (slot->state == DHCPV4_SERVER_ADDR_FREE) {
845 			new_slot = slot;
846 			break;
847 		}
848 	}
849 
850 	if (new_slot == NULL) {
851 		LOG_DBG("No more free addresses to assign, ICMP probing stopped");
852 		probe_ctx->slot = NULL;
853 		dhcpv4_server_timeout_recalc(ctx);
854 		goto out;
855 	}
856 
857 	if (dhcpv4_probe_address(ctx, new_slot) < 0) {
858 		probe_ctx->slot = NULL;
859 		dhcpv4_server_timeout_recalc(ctx);
860 		goto out;
861 	}
862 
863 	new_slot->state = DHCPV4_SERVER_ADDR_RESERVED;
864 	new_slot->expiry = sys_timepoint_calc(ADDRESS_PROBE_TIMEOUT);
865 	new_slot->client_id.len = probe_ctx->slot->client_id.len;
866 	memcpy(new_slot->client_id.buf, probe_ctx->slot->client_id.buf,
867 	       new_slot->client_id.len);
868 	new_slot->lease_time = probe_ctx->slot->lease_time;
869 
870 	probe_ctx->slot = new_slot;
871 
872 	dhcpv4_server_timeout_recalc(ctx);
873 
874 out:
875 	k_mutex_unlock(&server_lock);
876 
877 	return 0;
878 }
879 
dhcpv4_server_probing_init(struct dhcpv4_server_ctx * ctx)880 static int dhcpv4_server_probing_init(struct dhcpv4_server_ctx *ctx)
881 {
882 	return net_icmp_init_ctx(&ctx->probe_ctx.icmp_ctx,
883 				 NET_ICMPV4_ECHO_REPLY, 0,
884 				 echo_reply_handler);
885 }
886 
dhcpv4_server_probing_deinit(struct dhcpv4_server_ctx * ctx)887 static void dhcpv4_server_probing_deinit(struct dhcpv4_server_ctx *ctx)
888 {
889 	(void)net_icmp_cleanup_ctx(&ctx->probe_ctx.icmp_ctx);
890 }
891 
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)892 static int dhcpv4_server_probe_setup(struct dhcpv4_server_ctx *ctx,
893 				     struct dhcpv4_addr_slot *slot,
894 				     struct dhcp_msg *msg,
895 				     struct dhcpv4_parameter_request_list *params,
896 				     struct dhcpv4_client_id *client_id)
897 {
898 	int ret;
899 
900 	if (ctx->probe_ctx.slot != NULL) {
901 		return -EBUSY;
902 	}
903 
904 	ret = dhcpv4_probe_address(ctx, slot);
905 	if (ret < 0) {
906 		return ret;
907 	}
908 
909 	ctx->probe_ctx.slot = slot;
910 	ctx->probe_ctx.discovery = *msg;
911 	ctx->probe_ctx.params = *params;
912 	ctx->probe_ctx.client_id = *client_id;
913 
914 	return 0;
915 }
916 
dhcpv4_server_probe_timeout(struct dhcpv4_server_ctx * ctx,struct dhcpv4_addr_slot * slot)917 static void dhcpv4_server_probe_timeout(struct dhcpv4_server_ctx *ctx,
918 					struct dhcpv4_addr_slot *slot)
919 {
920 	/* Probe timer expired, send offer. */
921 	ctx->probe_ctx.slot = NULL;
922 
923 	(void)net_arp_clear_pending(ctx->iface, &slot->addr);
924 
925 	if (dhcpv4_send_offer(ctx, &ctx->probe_ctx.discovery, &slot->addr,
926 			      slot->lease_time, &ctx->probe_ctx.params,
927 			      &ctx->probe_ctx.client_id) < 0) {
928 		slot->state = DHCPV4_SERVER_ADDR_FREE;
929 		return;
930 	}
931 
932 	slot->expiry = sys_timepoint_calc(ADDRESS_RESERVED_TIMEOUT);
933 }
934 
dhcpv4_server_is_slot_probed(struct dhcpv4_server_ctx * ctx,struct dhcpv4_addr_slot * slot)935 static bool dhcpv4_server_is_slot_probed(struct dhcpv4_server_ctx *ctx,
936 					 struct dhcpv4_addr_slot *slot)
937 {
938 	return (ctx->probe_ctx.slot == slot);
939 }
940 #else /* defined(DHCPV4_SERVER_ICMP_PROBE) */
941 #define dhcpv4_server_probing_init(...) (0)
942 #define dhcpv4_server_probing_deinit(...)
943 #define dhcpv4_server_probe_setup(...) (-ENOTSUP)
944 #define dhcpv4_server_probe_timeout(...)
945 #define dhcpv4_server_is_slot_probed(...) (false)
946 #endif /* defined(DHCPV4_SERVER_ICMP_PROBE) */
947 
dhcpv4_handle_discover(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,uint8_t * options,uint8_t optlen)948 static void dhcpv4_handle_discover(struct dhcpv4_server_ctx *ctx,
949 				   struct dhcp_msg *msg, uint8_t *options,
950 				   uint8_t optlen)
951 {
952 	struct dhcpv4_parameter_request_list params = { 0 };
953 	struct dhcpv4_addr_slot *selected = NULL;
954 	struct dhcpv4_client_id client_id;
955 	bool probe = false;
956 	int ret;
957 
958 	ret = dhcpv4_get_client_id(msg, options, optlen, &client_id);
959 	if (ret < 0) {
960 		return;
961 	}
962 
963 	(void)dhcpv4_find_parameter_request_list_option(options, optlen, &params);
964 
965 	/* Address pool and address selection algorithm as
966 	 * described in 4.3.1
967 	 */
968 
969 	/* 1. Check for current bindings */
970 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
971 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
972 
973 		if ((slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
974 		     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED) &&
975 		    slot->client_id.len == client_id.len &&
976 		    memcmp(slot->client_id.buf, client_id.buf,
977 			    client_id.len) == 0) {
978 			if (slot->state == DHCPV4_SERVER_ADDR_RESERVED &&
979 			    dhcpv4_server_is_slot_probed(ctx, slot)) {
980 				LOG_DBG("ICMP probing in progress, ignore Discovery");
981 				return;
982 			}
983 
984 			/* Got match in current bindings. */
985 			selected = slot;
986 			break;
987 		}
988 		struct in_addr addr = { 0 };
989 
990 		if (slot->state == DHCPV4_SERVER_ADDR_FREE &&
991 		    address_provider_callback) {
992 			ret = address_provider_callback(ctx->iface, &client_id, &addr,
993 							address_provider_callback_user_data);
994 			if (ret == 0) {
995 				selected = slot;
996 				slot->addr = addr;
997 			}
998 			break;
999 		}
1000 	}
1001 
1002 	/* 2. Skipped, for now expired/released entries are forgotten. */
1003 
1004 	/* 3. Check Requested IP Address option. */
1005 	if (selected == NULL) {
1006 		struct in_addr requested_ip;
1007 
1008 		ret = dhcpv4_find_requested_ip_option(options, optlen,
1009 						      &requested_ip);
1010 		if (ret == 0) {
1011 			for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1012 				struct dhcpv4_addr_slot *slot =
1013 							&ctx->addr_pool[i];
1014 
1015 				if (net_ipv4_addr_cmp(&slot->addr,
1016 						      &requested_ip) &&
1017 				    slot->state == DHCPV4_SERVER_ADDR_FREE) {
1018 					/* Requested address is free. */
1019 					selected = slot;
1020 					probe = true;
1021 					break;
1022 				}
1023 			}
1024 		}
1025 	}
1026 
1027 	/* 4. Allocate new address from pool, if available. */
1028 	if (selected == NULL) {
1029 		struct in_addr giaddr;
1030 
1031 		memcpy(&giaddr, msg->giaddr, sizeof(giaddr));
1032 		if (!net_ipv4_is_addr_unspecified(&giaddr)) {
1033 			/* Only addresses in local subnet supported for now. */
1034 			return;
1035 		}
1036 
1037 		for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1038 			struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1039 
1040 			if (slot->state == DHCPV4_SERVER_ADDR_FREE) {
1041 				/* Requested address is free. */
1042 				selected = slot;
1043 				probe = true;
1044 				break;
1045 			}
1046 		}
1047 	}
1048 
1049 	/* In case no free address slot was found, as a last resort, try to
1050 	 * reuse the oldest declined entry, if present.
1051 	 */
1052 	if (selected == NULL) {
1053 		for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1054 			struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1055 
1056 			if (slot->state != DHCPV4_SERVER_ADDR_DECLINED) {
1057 				continue;
1058 			}
1059 
1060 			/* Find first to expire (oldest) entry. */
1061 			if ((selected == NULL) ||
1062 			    (sys_timepoint_cmp(slot->expiry,
1063 					       selected->expiry) < 0)) {
1064 				selected = slot;
1065 				probe = true;
1066 			}
1067 		}
1068 	}
1069 
1070 	if (selected == NULL) {
1071 		LOG_ERR("No free address found in address pool");
1072 	} else {
1073 		uint32_t lease_time = dhcpv4_get_lease_time(options, optlen);
1074 
1075 		if (IS_ENABLED(DHCPV4_SERVER_ICMP_PROBE) && probe) {
1076 			if (dhcpv4_server_probe_setup(ctx, selected, msg,
1077 						      &params, &client_id) < 0) {
1078 				/* Probing context already in use or failed to
1079 				 * send probe, ignore Discovery for now and wait
1080 				 * for retransmission.
1081 				 */
1082 				return;
1083 			}
1084 
1085 			selected->expiry =
1086 				sys_timepoint_calc(ADDRESS_PROBE_TIMEOUT);
1087 		} else {
1088 			if (dhcpv4_send_offer(ctx, msg, &selected->addr,
1089 					      lease_time, &params, &client_id) < 0) {
1090 				return;
1091 			}
1092 
1093 			selected->expiry =
1094 				sys_timepoint_calc(ADDRESS_RESERVED_TIMEOUT);
1095 		}
1096 
1097 		LOG_DBG("DHCPv4 processing Discover - reserved %s",
1098 			net_sprint_ipv4_addr(&selected->addr));
1099 
1100 		selected->state = DHCPV4_SERVER_ADDR_RESERVED;
1101 		selected->client_id.len = client_id.len;
1102 		memcpy(selected->client_id.buf, client_id.buf, client_id.len);
1103 		selected->lease_time = lease_time;
1104 		dhcpv4_server_timeout_recalc(ctx);
1105 	}
1106 }
1107 
dhcpv4_handle_request(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,uint8_t * options,uint8_t optlen)1108 static void dhcpv4_handle_request(struct dhcpv4_server_ctx *ctx,
1109 				  struct dhcp_msg *msg, uint8_t *options,
1110 				  uint8_t optlen)
1111 {
1112 	struct dhcpv4_parameter_request_list params = { 0 };
1113 	struct dhcpv4_addr_slot *selected = NULL;
1114 	struct dhcpv4_client_id client_id;
1115 	struct in_addr requested_ip, server_id, ciaddr, giaddr;
1116 	int ret;
1117 
1118 	memcpy(&ciaddr, msg->ciaddr, sizeof(ciaddr));
1119 	memcpy(&giaddr, msg->giaddr, sizeof(giaddr));
1120 
1121 	if (!net_ipv4_is_addr_unspecified(&giaddr)) {
1122 		/* Only addresses in local subnet supported for now. */
1123 		return;
1124 	}
1125 
1126 	ret = dhcpv4_get_client_id(msg, options, optlen, &client_id);
1127 	if (ret < 0) {
1128 		/* Failed to obtain Client ID, ignore. */
1129 		return;
1130 	}
1131 
1132 	(void)dhcpv4_find_parameter_request_list_option(options, optlen, &params);
1133 
1134 	ret = dhcpv4_find_server_id_option(options, optlen, &server_id);
1135 	if (ret == 0) {
1136 		/* Server ID present, Request generated during SELECTING. */
1137 		if (!net_ipv4_addr_cmp(&ctx->server_addr, &server_id)) {
1138 			/* Not for us, ignore. */
1139 			return;
1140 		}
1141 
1142 		ret = dhcpv4_find_requested_ip_option(options, optlen,
1143 						      &requested_ip);
1144 		if (ret < 0) {
1145 			/* Requested IP missing, ignore. */
1146 			return;
1147 		}
1148 
1149 		if (!net_ipv4_is_addr_unspecified(&ciaddr)) {
1150 			/* ciaddr MUST be zero */
1151 			return;
1152 		}
1153 
1154 		for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1155 			struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1156 
1157 			if (net_ipv4_addr_cmp(&slot->addr, &requested_ip) &&
1158 			    slot->client_id.len == client_id.len &&
1159 			    memcmp(slot->client_id.buf, client_id.buf,
1160 				   client_id.len) == 0 &&
1161 			    (slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
1162 			     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED)) {
1163 				selected = slot;
1164 				break;
1165 			}
1166 		}
1167 
1168 		if (selected == NULL) {
1169 			LOG_ERR("No valid slot found for DHCPv4 Request");
1170 		} else {
1171 			uint32_t lease_time = dhcpv4_get_lease_time(options, optlen);
1172 
1173 			if (dhcpv4_send_ack(ctx, msg, &selected->addr, lease_time,
1174 					    &params, &client_id, false) < 0) {
1175 				return;
1176 			}
1177 
1178 			LOG_DBG("DHCPv4 processing Request - allocated %s",
1179 				net_sprint_ipv4_addr(&selected->addr));
1180 
1181 			selected->lease_time = lease_time;
1182 			selected->expiry = sys_timepoint_calc(
1183 							K_SECONDS(lease_time));
1184 			selected->state = DHCPV4_SERVER_ADDR_ALLOCATED;
1185 			dhcpv4_server_timeout_recalc(ctx);
1186 		}
1187 
1188 		return;
1189 	}
1190 
1191 	/* No server ID option - check requested address. */
1192 	ret = dhcpv4_find_requested_ip_option(options, optlen, &requested_ip);
1193 	if (ret == 0) {
1194 		/* Requested IP present, Request generated during INIT-REBOOT. */
1195 		if (!net_ipv4_is_addr_unspecified(&ciaddr)) {
1196 			/* ciaddr MUST be zero */
1197 			return;
1198 		}
1199 
1200 		if (!net_if_ipv4_addr_mask_cmp(ctx->iface, &requested_ip)) {
1201 			/* Wrong subnet. */
1202 			dhcpv4_send_nak(ctx, msg, &client_id);
1203 		}
1204 
1205 		for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1206 			struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1207 
1208 			if (slot->client_id.len == client_id.len &&
1209 			    memcmp(slot->client_id.buf, client_id.buf,
1210 				   client_id.len) == 0 &&
1211 			    (slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
1212 			     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED)) {
1213 				selected = slot;
1214 				break;
1215 			}
1216 		}
1217 
1218 		if (selected != NULL) {
1219 			if (net_ipv4_addr_cmp(&selected->addr, &requested_ip)) {
1220 				uint32_t lease_time = dhcpv4_get_lease_time(
1221 								options, optlen);
1222 
1223 				if (dhcpv4_send_ack(ctx, msg, &selected->addr,
1224 						    lease_time, &params,
1225 						    &client_id, false) < 0) {
1226 					return;
1227 				}
1228 
1229 				selected->lease_time = lease_time;
1230 				selected->expiry = sys_timepoint_calc(
1231 							K_SECONDS(lease_time));
1232 				dhcpv4_server_timeout_recalc(ctx);
1233 			} else {
1234 				dhcpv4_send_nak(ctx, msg, &client_id);
1235 			}
1236 		} else if (IS_ENABLED(CONFIG_NET_DHCPV4_SERVER_NAK_UNRECOGNIZED_REQUESTS)) {
1237 			dhcpv4_send_nak(ctx, msg, &client_id);
1238 		}
1239 
1240 		/* No notion of the client, remain silent. */
1241 		return;
1242 	}
1243 
1244 	/* Neither server ID or requested IP set, Request generated during
1245 	 * RENEWING or REBINDING.
1246 	 */
1247 
1248 	if (!net_if_ipv4_addr_mask_cmp(ctx->iface, &ciaddr)) {
1249 		/* Wrong subnet. */
1250 		dhcpv4_send_nak(ctx, msg, &client_id);
1251 	}
1252 
1253 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1254 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1255 
1256 		if (net_ipv4_addr_cmp(&slot->addr, &ciaddr)) {
1257 			selected = slot;
1258 			break;
1259 		}
1260 	}
1261 
1262 	if (selected != NULL) {
1263 		if (selected->state == DHCPV4_SERVER_ADDR_ALLOCATED &&
1264 		    selected->client_id.len == client_id.len &&
1265 		    memcmp(selected->client_id.buf, client_id.buf,
1266 			   client_id.len) == 0) {
1267 			uint32_t lease_time = dhcpv4_get_lease_time(
1268 								options, optlen);
1269 
1270 			if (dhcpv4_send_ack(ctx, msg, &ciaddr, lease_time,
1271 					    &params, &client_id, false) < 0) {
1272 				return;
1273 			}
1274 
1275 			selected->lease_time = lease_time;
1276 			selected->expiry = sys_timepoint_calc(
1277 							K_SECONDS(lease_time));
1278 			dhcpv4_server_timeout_recalc(ctx);
1279 		} else {
1280 			dhcpv4_send_nak(ctx, msg, &client_id);
1281 		}
1282 	}
1283 }
1284 
dhcpv4_handle_decline(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,uint8_t * options,uint8_t optlen)1285 static void dhcpv4_handle_decline(struct dhcpv4_server_ctx *ctx,
1286 				  struct dhcp_msg *msg, uint8_t *options,
1287 				  uint8_t optlen)
1288 {
1289 	struct dhcpv4_client_id client_id;
1290 	struct in_addr requested_ip, server_id;
1291 	int ret;
1292 
1293 	ret = dhcpv4_find_server_id_option(options, optlen, &server_id);
1294 	if (ret < 0) {
1295 		/* No server ID, ignore. */
1296 		return;
1297 	}
1298 
1299 	if (!net_ipv4_addr_cmp(&ctx->server_addr, &server_id)) {
1300 		/* Not for us, ignore. */
1301 		return;
1302 	}
1303 
1304 	ret = dhcpv4_get_client_id(msg, options, optlen, &client_id);
1305 	if (ret < 0) {
1306 		/* Failed to obtain Client ID, ignore. */
1307 		return;
1308 	}
1309 
1310 	ret = dhcpv4_find_requested_ip_option(options, optlen,
1311 						&requested_ip);
1312 	if (ret < 0) {
1313 		/* Requested IP missing, ignore. */
1314 		return;
1315 	}
1316 
1317 	LOG_ERR("Received DHCPv4 Decline for %s (address already in use)",
1318 		net_sprint_ipv4_addr(&requested_ip));
1319 
1320 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1321 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1322 
1323 		if (net_ipv4_addr_cmp(&slot->addr, &requested_ip) &&
1324 		    slot->client_id.len == client_id.len &&
1325 		    memcmp(slot->client_id.buf, client_id.buf,
1326 				client_id.len) == 0 &&
1327 		    (slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
1328 		     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED)) {
1329 			slot->state = DHCPV4_SERVER_ADDR_DECLINED;
1330 			slot->expiry =
1331 				sys_timepoint_calc(ADDRESS_DECLINED_TIMEOUT);
1332 			dhcpv4_server_timeout_recalc(ctx);
1333 			break;
1334 		}
1335 	}
1336 }
1337 
dhcpv4_handle_release(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,uint8_t * options,uint8_t optlen)1338 static void dhcpv4_handle_release(struct dhcpv4_server_ctx *ctx,
1339 				  struct dhcp_msg *msg, uint8_t *options,
1340 				  uint8_t optlen)
1341 {
1342 	struct dhcpv4_client_id client_id;
1343 	struct in_addr ciaddr, server_id;
1344 	int ret;
1345 
1346 	ret = dhcpv4_find_server_id_option(options, optlen, &server_id);
1347 	if (ret < 0) {
1348 		/* No server ID, ignore. */
1349 		return;
1350 	}
1351 
1352 	if (!net_ipv4_addr_cmp(&ctx->server_addr, &server_id)) {
1353 		/* Not for us, ignore. */
1354 		return;
1355 	}
1356 
1357 	ret = dhcpv4_get_client_id(msg, options, optlen, &client_id);
1358 	if (ret < 0) {
1359 		/* Failed to obtain Client ID, ignore. */
1360 		return;
1361 	}
1362 
1363 	memcpy(&ciaddr, msg->ciaddr, sizeof(ciaddr));
1364 
1365 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1366 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1367 
1368 		if (net_ipv4_addr_cmp(&slot->addr, &ciaddr) &&
1369 		    slot->client_id.len == client_id.len &&
1370 		    memcmp(slot->client_id.buf, client_id.buf,
1371 				client_id.len) == 0 &&
1372 		    (slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
1373 		     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED)) {
1374 			LOG_DBG("DHCPv4 processing Release - %s",
1375 				net_sprint_ipv4_addr(&slot->addr));
1376 
1377 			slot->state = DHCPV4_SERVER_ADDR_FREE;
1378 			slot->expiry = sys_timepoint_calc(K_FOREVER);
1379 			dhcpv4_server_timeout_recalc(ctx);
1380 			break;
1381 		}
1382 	}
1383 }
1384 
dhcpv4_handle_inform(struct dhcpv4_server_ctx * ctx,struct dhcp_msg * msg,uint8_t * options,uint8_t optlen)1385 static void dhcpv4_handle_inform(struct dhcpv4_server_ctx *ctx,
1386 				 struct dhcp_msg *msg, uint8_t *options,
1387 				 uint8_t optlen)
1388 {
1389 	struct dhcpv4_parameter_request_list params = { 0 };
1390 
1391 	(void)dhcpv4_find_parameter_request_list_option(options, optlen, &params);
1392 	(void)dhcpv4_send_ack(ctx, msg, (struct in_addr *)msg->ciaddr, 0,
1393 			      &params, NULL, true);
1394 }
1395 
1396 /* Server core. */
1397 
dhcpv4_server_timeout(struct k_work * work)1398 static void dhcpv4_server_timeout(struct k_work *work)
1399 {
1400 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
1401 	struct dhcpv4_server_ctx *ctx =
1402 		CONTAINER_OF(dwork, struct dhcpv4_server_ctx, timeout_work);
1403 
1404 	k_mutex_lock(&server_lock, K_FOREVER);
1405 
1406 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1407 		struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
1408 
1409 		if ((slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
1410 		     slot->state == DHCPV4_SERVER_ADDR_ALLOCATED) &&
1411 		     sys_timepoint_expired(slot->expiry)) {
1412 			if (slot->state == DHCPV4_SERVER_ADDR_RESERVED &&
1413 			    dhcpv4_server_is_slot_probed(ctx, slot)) {
1414 				dhcpv4_server_probe_timeout(ctx, slot);
1415 			} else {
1416 				LOG_DBG("Address %s expired",
1417 					net_sprint_ipv4_addr(&slot->addr));
1418 				slot->state = DHCPV4_SERVER_ADDR_FREE;
1419 			}
1420 		}
1421 
1422 		if (slot->state == DHCPV4_SERVER_ADDR_DECLINED &&
1423 		    sys_timepoint_expired(slot->expiry)) {
1424 			slot->state = DHCPV4_SERVER_ADDR_FREE;
1425 		}
1426 	}
1427 
1428 	dhcpv4_server_timeout_recalc(ctx);
1429 
1430 	k_mutex_unlock(&server_lock);
1431 }
1432 
dhcpv4_process_data(struct dhcpv4_server_ctx * ctx,uint8_t * data,size_t datalen)1433 static void dhcpv4_process_data(struct dhcpv4_server_ctx *ctx, uint8_t *data,
1434 				size_t datalen)
1435 {
1436 	struct dhcp_msg *msg;
1437 	uint8_t msgtype;
1438 	int ret;
1439 
1440 	if (datalen < sizeof(struct dhcp_msg)) {
1441 		LOG_DBG("DHCPv4 server malformed message");
1442 		return;
1443 	}
1444 
1445 	msg = (struct dhcp_msg *)data;
1446 
1447 	if (msg->op != DHCPV4_MSG_BOOT_REQUEST) {
1448 		/* Silently drop messages other than BOOTREQUEST */
1449 		return;
1450 	}
1451 
1452 	data += sizeof(struct dhcp_msg);
1453 	datalen -= sizeof(struct dhcp_msg);
1454 
1455 	/* Skip server hostname/filename/option cookie */
1456 	if (datalen < (SIZE_OF_SNAME + SIZE_OF_FILE + SIZE_OF_MAGIC_COOKIE)) {
1457 		return;
1458 	}
1459 
1460 	data += SIZE_OF_SNAME + SIZE_OF_FILE + SIZE_OF_MAGIC_COOKIE;
1461 	datalen -= SIZE_OF_SNAME + SIZE_OF_FILE + SIZE_OF_MAGIC_COOKIE;
1462 
1463 	/* Search options for DHCP message type. */
1464 	ret = dhcpv4_find_message_type_option(data, datalen, &msgtype);
1465 	if (ret < 0) {
1466 		LOG_ERR("No message type option");
1467 		return;
1468 	}
1469 
1470 	k_mutex_lock(&server_lock, K_FOREVER);
1471 
1472 	switch (msgtype) {
1473 	case NET_DHCPV4_MSG_TYPE_DISCOVER:
1474 		dhcpv4_handle_discover(ctx, msg, data, datalen);
1475 		break;
1476 	case NET_DHCPV4_MSG_TYPE_REQUEST:
1477 		dhcpv4_handle_request(ctx, msg, data, datalen);
1478 		break;
1479 	case NET_DHCPV4_MSG_TYPE_DECLINE:
1480 		dhcpv4_handle_decline(ctx, msg, data, datalen);
1481 		break;
1482 	case NET_DHCPV4_MSG_TYPE_RELEASE:
1483 		dhcpv4_handle_release(ctx, msg, data, datalen);
1484 		break;
1485 	case NET_DHCPV4_MSG_TYPE_INFORM:
1486 		dhcpv4_handle_inform(ctx, msg, data, datalen);
1487 		break;
1488 
1489 	case NET_DHCPV4_MSG_TYPE_OFFER:
1490 	case NET_DHCPV4_MSG_TYPE_ACK:
1491 	case NET_DHCPV4_MSG_TYPE_NAK:
1492 	default:
1493 		/* Ignore server initiated and unknown message types. */
1494 		break;
1495 	}
1496 
1497 	k_mutex_unlock(&server_lock);
1498 }
1499 
dhcpv4_server_cb(struct net_socket_service_event * evt)1500 static void dhcpv4_server_cb(struct net_socket_service_event *evt)
1501 {
1502 	struct dhcpv4_server_ctx *ctx = NULL;
1503 	uint8_t recv_buf[NET_IPV4_MTU];
1504 	int ret;
1505 
1506 	for (int i = 0; i < ARRAY_SIZE(server_ctx); i++) {
1507 		if (server_ctx[i].sock == evt->event.fd) {
1508 			ctx = &server_ctx[i];
1509 			break;
1510 		}
1511 	}
1512 
1513 	if (ctx == NULL) {
1514 		LOG_ERR("No DHCPv4 server context found for given FD.");
1515 		return;
1516 	}
1517 
1518 	if (evt->event.revents & ZSOCK_POLLERR) {
1519 		LOG_ERR("DHCPv4 server poll revents error");
1520 		net_dhcpv4_server_stop(ctx->iface);
1521 		return;
1522 	}
1523 
1524 	if (!(evt->event.revents & ZSOCK_POLLIN)) {
1525 		return;
1526 	}
1527 
1528 	ret = zsock_recvfrom(evt->event.fd, recv_buf, sizeof(recv_buf),
1529 			     ZSOCK_MSG_DONTWAIT, NULL, 0);
1530 	if (ret < 0) {
1531 		if (errno == EAGAIN) {
1532 			return;
1533 		}
1534 
1535 		LOG_ERR("DHCPv4 server recv error, %d", errno);
1536 		net_dhcpv4_server_stop(ctx->iface);
1537 		return;
1538 	}
1539 
1540 	dhcpv4_process_data(ctx, recv_buf, ret);
1541 }
1542 
1543 NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(dhcpv4_server, dhcpv4_server_cb,
1544 				      CONFIG_NET_DHCPV4_SERVER_INSTANCES);
1545 
net_dhcpv4_server_start(struct net_if * iface,struct in_addr * base_addr)1546 int net_dhcpv4_server_start(struct net_if *iface, struct in_addr *base_addr)
1547 {
1548 	struct sockaddr_in addr = {
1549 		.sin_family = AF_INET,
1550 		.sin_addr = INADDR_ANY_INIT,
1551 		.sin_port = htons(DHCPV4_SERVER_PORT),
1552 	};
1553 	struct ifreq ifreq = { 0 };
1554 	int ret, sock = -1, slot = -1;
1555 	const struct in_addr *server_addr;
1556 	struct in_addr netmask;
1557 
1558 	if (iface == NULL || base_addr == NULL) {
1559 		return -EINVAL;
1560 	}
1561 
1562 	if (!net_if_ipv4_addr_mask_cmp(iface, base_addr)) {
1563 		LOG_ERR("Address pool does not belong to the interface subnet.");
1564 		return -EINVAL;
1565 	}
1566 
1567 	server_addr = net_if_ipv4_select_src_addr(iface, base_addr);
1568 	if (server_addr == NULL) {
1569 		LOG_ERR("Failed to obtain a valid server address.");
1570 		return -EINVAL;
1571 	}
1572 
1573 	if ((htonl(server_addr->s_addr) >= htonl(base_addr->s_addr)) &&
1574 	    (htonl(server_addr->s_addr) <
1575 	     htonl(base_addr->s_addr) + CONFIG_NET_DHCPV4_SERVER_ADDR_COUNT)) {
1576 		LOG_ERR("Address pool overlaps with server address.");
1577 		return -EINVAL;
1578 	}
1579 
1580 	netmask = net_if_ipv4_get_netmask_by_addr(iface, server_addr);
1581 	if (net_ipv4_is_addr_unspecified(&netmask)) {
1582 		LOG_ERR("Failed to obtain subnet mask.");
1583 		return -EINVAL;
1584 	}
1585 
1586 	k_mutex_lock(&server_lock, K_FOREVER);
1587 
1588 	for (int i = 0; i < ARRAY_SIZE(server_ctx); i++) {
1589 		if (server_ctx[i].iface != NULL) {
1590 			if (server_ctx[i].iface == iface) {
1591 				LOG_ERR("DHCPv4 server instance already running.");
1592 				ret = -EALREADY;
1593 				goto error;
1594 			}
1595 		} else {
1596 			if (slot < 0) {
1597 				slot = i;
1598 			}
1599 		}
1600 	}
1601 
1602 	if (slot < 0) {
1603 		LOG_ERR("No free DHCPv4 server instance.");
1604 		ret = -ENOMEM;
1605 		goto error;
1606 	}
1607 
1608 	ret = net_if_get_name(iface, ifreq.ifr_name, sizeof(ifreq.ifr_name));
1609 	if (ret < 0) {
1610 		LOG_ERR("Failed to obtain interface name.");
1611 		goto error;
1612 	}
1613 
1614 	sock = zsock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1615 	if (sock < 0) {
1616 		ret = -errno;
1617 		LOG_ERR("Failed to create DHCPv4 server socket, %d", ret);
1618 		goto error;
1619 	}
1620 
1621 	ret = zsock_setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq,
1622 			       sizeof(ifreq));
1623 	if (ret < 0) {
1624 		ret = -errno;
1625 		LOG_ERR("Failed to bind DHCPv4 server socket with interface, %d",
1626 			ret);
1627 		goto error;
1628 	}
1629 
1630 	ret = zsock_bind(sock, (struct sockaddr *)&addr, sizeof(addr));
1631 	if (ret < 0) {
1632 		ret = -errno;
1633 		LOG_ERR("Failed to bind DHCPv4 server socket, %d", ret);
1634 		goto error;
1635 	}
1636 
1637 	fds[slot].fd = sock;
1638 	fds[slot].events = ZSOCK_POLLIN;
1639 
1640 	server_ctx[slot].iface = iface;
1641 	server_ctx[slot].sock = sock;
1642 	server_ctx[slot].server_addr = *server_addr;
1643 	server_ctx[slot].netmask = netmask;
1644 
1645 	k_work_init_delayable(&server_ctx[slot].timeout_work,
1646 			      dhcpv4_server_timeout);
1647 
1648 	LOG_DBG("Started DHCPv4 server, address pool:");
1649 	for (int i = 0; i < ARRAY_SIZE(server_ctx[slot].addr_pool); i++) {
1650 		server_ctx[slot].addr_pool[i].state = DHCPV4_SERVER_ADDR_FREE;
1651 		server_ctx[slot].addr_pool[i].addr.s_addr =
1652 					htonl(ntohl(base_addr->s_addr) + i);
1653 
1654 		LOG_DBG("\t%2d: %s", i,
1655 			net_sprint_ipv4_addr(
1656 				&server_ctx[slot].addr_pool[i].addr));
1657 	}
1658 
1659 	ret = dhcpv4_server_probing_init(&server_ctx[slot]);
1660 	if (ret < 0) {
1661 		LOG_ERR("Failed to register probe handler, %d", ret);
1662 		goto cleanup;
1663 	}
1664 
1665 	ret = net_socket_service_register(&dhcpv4_server, fds, ARRAY_SIZE(fds),
1666 					  NULL);
1667 	if (ret < 0) {
1668 		LOG_ERR("Failed to register socket service, %d", ret);
1669 		dhcpv4_server_probing_deinit(&server_ctx[slot]);
1670 		goto cleanup;
1671 	}
1672 
1673 	k_mutex_unlock(&server_lock);
1674 
1675 	return 0;
1676 
1677 cleanup:
1678 	memset(&server_ctx[slot], 0, sizeof(server_ctx[slot]));
1679 	fds[slot].fd = -1;
1680 
1681 error:
1682 	if (sock >= 0) {
1683 		(void)zsock_close(sock);
1684 	}
1685 
1686 	k_mutex_unlock(&server_lock);
1687 
1688 	return ret;
1689 }
1690 
net_dhcpv4_server_stop(struct net_if * iface)1691 int net_dhcpv4_server_stop(struct net_if *iface)
1692 {
1693 	struct k_work_sync sync;
1694 	int slot = -1;
1695 	int ret = 0;
1696 	bool service_stop = true;
1697 
1698 	if (iface == NULL) {
1699 		return -EINVAL;
1700 	}
1701 
1702 	k_mutex_lock(&server_lock, K_FOREVER);
1703 
1704 	for (int i = 0; i < ARRAY_SIZE(server_ctx); i++) {
1705 		if (server_ctx[i].iface == iface) {
1706 			slot = i;
1707 			break;
1708 		}
1709 	}
1710 
1711 	if (slot < 0) {
1712 		ret = -ENOENT;
1713 		goto out;
1714 	}
1715 
1716 	fds[slot].fd = -1;
1717 	(void)zsock_close(server_ctx[slot].sock);
1718 
1719 	dhcpv4_server_probing_deinit(&server_ctx[slot]);
1720 	k_work_cancel_delayable_sync(&server_ctx[slot].timeout_work, &sync);
1721 
1722 	memset(&server_ctx[slot], 0, sizeof(server_ctx[slot]));
1723 
1724 	for (int i = 0; i < ARRAY_SIZE(fds); i++) {
1725 		if (fds[i].fd >= 0) {
1726 			service_stop = false;
1727 			break;
1728 		}
1729 	}
1730 
1731 	if (service_stop) {
1732 		ret = net_socket_service_unregister(&dhcpv4_server);
1733 	} else {
1734 		ret = net_socket_service_register(&dhcpv4_server, fds,
1735 						  ARRAY_SIZE(fds), NULL);
1736 	}
1737 
1738 out:
1739 	k_mutex_unlock(&server_lock);
1740 
1741 	return ret;
1742 }
1743 
dhcpv4_server_foreach_lease_on_ctx(struct dhcpv4_server_ctx * ctx,net_dhcpv4_lease_cb_t cb,void * user_data)1744 static void dhcpv4_server_foreach_lease_on_ctx(struct dhcpv4_server_ctx *ctx,
1745 						 net_dhcpv4_lease_cb_t cb,
1746 						 void *user_data)
1747 {
1748 	for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
1749 		struct dhcpv4_addr_slot *addr = &ctx->addr_pool[i];
1750 
1751 		if (addr->state != DHCPV4_SERVER_ADDR_FREE) {
1752 			cb(ctx->iface, addr, user_data);
1753 		}
1754 	}
1755 }
1756 
net_dhcpv4_server_foreach_lease(struct net_if * iface,net_dhcpv4_lease_cb_t cb,void * user_data)1757 int net_dhcpv4_server_foreach_lease(struct net_if *iface,
1758 				    net_dhcpv4_lease_cb_t cb,
1759 				    void *user_data)
1760 {
1761 	int slot = -1;
1762 	int ret = 0;
1763 
1764 	k_mutex_lock(&server_lock, K_FOREVER);
1765 
1766 	if (iface == NULL) {
1767 		for (int i = 0; i < ARRAY_SIZE(server_ctx); i++) {
1768 			if (server_ctx[i].iface != NULL) {
1769 				dhcpv4_server_foreach_lease_on_ctx(
1770 						&server_ctx[i], cb, user_data);
1771 			}
1772 		}
1773 
1774 		return 0;
1775 	}
1776 
1777 	for (int i = 0; i < ARRAY_SIZE(server_ctx); i++) {
1778 		if (server_ctx[i].iface == iface) {
1779 			slot = i;
1780 			break;
1781 		}
1782 	}
1783 
1784 	if (slot < 0) {
1785 		ret = -ENOENT;
1786 		goto out;
1787 	}
1788 
1789 	dhcpv4_server_foreach_lease_on_ctx(&server_ctx[slot], cb, user_data);
1790 
1791 out:
1792 	k_mutex_unlock(&server_lock);
1793 
1794 	return ret;
1795 }
1796 
net_dhcpv4_server_set_provider_cb(net_dhcpv4_server_provider_cb_t cb,void * user_data)1797 void net_dhcpv4_server_set_provider_cb(net_dhcpv4_server_provider_cb_t cb, void *user_data)
1798 {
1799 	address_provider_callback_user_data = user_data;
1800 	address_provider_callback = cb;
1801 }
1802 
net_dhcpv4_server_init(void)1803 void net_dhcpv4_server_init(void)
1804 {
1805 	address_provider_callback = NULL;
1806 	address_provider_callback_user_data = NULL;
1807 
1808 	for (int i = 0; i < ARRAY_SIZE(fds); i++) {
1809 		fds[i].fd = -1;
1810 	}
1811 }
1812