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, ¶ms);
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 ¶ms, &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, ¶ms, &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, ¶ms);
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 ¶ms, &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, ¶ms,
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 ¶ms, &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, ¶ms);
1384 (void)dhcpv4_send_ack(ctx, msg, (struct in_addr *)msg->ciaddr, 0,
1385 ¶ms, 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