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