1 /** @file
2 * @brief DHCPv4 client related functions
3 */
4
5 /*
6 * Copyright (c) 2017 ARM Ltd.
7 * Copyright (c) 2016 Intel Corporation
8 * Copyright (c) 2018 Vincent van der Locht
9 *
10 * SPDX-License-Identifier: Apache-2.0
11 */
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(net_dhcpv4, CONFIG_NET_DHCPV4_LOG_LEVEL);
15
16 #include <errno.h>
17 #include <inttypes.h>
18 #include <stdbool.h>
19 #include <zephyr/random/random.h>
20 #include <zephyr/net/net_core.h>
21 #include <zephyr/net/net_pkt.h>
22 #include <zephyr/net/net_if.h>
23 #include <zephyr/net/net_mgmt.h>
24 #include "net_private.h"
25
26 #include <zephyr/net/udp.h>
27 #include "udp_internal.h"
28 #include <zephyr/net/dhcpv4.h>
29 #include <zephyr/net/dns_resolve.h>
30
31 #include "dhcpv4.h"
32 #include "ipv4.h"
33 #include "net_stats.h"
34
35 #include <zephyr/sys/slist.h>
36 #include <zephyr/sys/util.h>
37
38 #define PKT_WAIT_TIME K_SECONDS(1)
39
40 static K_MUTEX_DEFINE(lock);
41
42 static sys_slist_t dhcpv4_ifaces;
43 static struct k_work_delayable timeout_work;
44
45 static struct net_mgmt_event_callback mgmt4_cb;
46
47 #if defined(CONFIG_NET_DHCPV4_OPTION_CALLBACKS)
48 static sys_slist_t option_callbacks;
49 #endif
50
51 /* RFC 1497 [17] */
52 static const uint8_t magic_cookie[4] = { 0x63, 0x82, 0x53, 0x63 };
53
54 /* Add magic cookie to DCHPv4 messages */
dhcpv4_add_cookie(struct net_pkt * pkt)55 static inline bool dhcpv4_add_cookie(struct net_pkt *pkt)
56 {
57 if (net_pkt_write(pkt, (void *)magic_cookie,
58 ARRAY_SIZE(magic_cookie))) {
59 return false;
60 }
61
62 return true;
63 }
64
65 /* Add a an option with the form OPTION LENGTH VALUE. */
dhcpv4_add_option_length_value(struct net_pkt * pkt,uint8_t option,uint8_t size,const void * value)66 static bool dhcpv4_add_option_length_value(struct net_pkt *pkt, uint8_t option,
67 uint8_t size, const void *value)
68 {
69 if (net_pkt_write_u8(pkt, option) ||
70 net_pkt_write_u8(pkt, size) ||
71 net_pkt_write(pkt, value, size)) {
72 return false;
73 }
74
75 return true;
76 }
77
78 /* Add DHCPv4 message type */
dhcpv4_add_msg_type(struct net_pkt * pkt,uint8_t type)79 static bool dhcpv4_add_msg_type(struct net_pkt *pkt, uint8_t type)
80 {
81 return dhcpv4_add_option_length_value(pkt, DHCPV4_OPTIONS_MSG_TYPE,
82 1, &type);
83 }
84
85 /* Add DHCPv4 minimum required options for server to reply.
86 * Can be added more if needed.
87 */
dhcpv4_add_req_options(struct net_pkt * pkt)88 static bool dhcpv4_add_req_options(struct net_pkt *pkt)
89 {
90 static uint8_t data[3] = { DHCPV4_OPTIONS_SUBNET_MASK,
91 DHCPV4_OPTIONS_ROUTER,
92 DHCPV4_OPTIONS_DNS_SERVER };
93
94 return dhcpv4_add_option_length_value(pkt, DHCPV4_OPTIONS_REQ_LIST,
95 ARRAY_SIZE(data), data);
96 }
97
dhcpv4_add_server_id(struct net_pkt * pkt,const struct in_addr * addr)98 static bool dhcpv4_add_server_id(struct net_pkt *pkt,
99 const struct in_addr *addr)
100 {
101 return dhcpv4_add_option_length_value(pkt, DHCPV4_OPTIONS_SERVER_ID,
102 4, addr->s4_addr);
103 }
104
dhcpv4_add_req_ipaddr(struct net_pkt * pkt,const struct in_addr * addr)105 static bool dhcpv4_add_req_ipaddr(struct net_pkt *pkt,
106 const struct in_addr *addr)
107 {
108 return dhcpv4_add_option_length_value(pkt, DHCPV4_OPTIONS_REQ_IPADDR,
109 4, addr->s4_addr);
110 }
111
112 #if defined(CONFIG_NET_HOSTNAME_ENABLE)
dhcpv4_add_hostname(struct net_pkt * pkt,const char * hostname,const size_t size)113 static bool dhcpv4_add_hostname(struct net_pkt *pkt,
114 const char *hostname, const size_t size)
115 {
116 return dhcpv4_add_option_length_value(pkt, DHCPV4_OPTIONS_HOST_NAME,
117 size, hostname);
118 }
119 #endif
120
121 /* Add DHCPv4 Options end, rest of the message can be padded wit zeros */
dhcpv4_add_end(struct net_pkt * pkt)122 static inline bool dhcpv4_add_end(struct net_pkt *pkt)
123 {
124 if (net_pkt_write_u8(pkt, DHCPV4_OPTIONS_END)) {
125 return false;
126 }
127
128 return true;
129 }
130
131 /* File is empty ATM */
dhcpv4_add_file(struct net_pkt * pkt)132 static inline bool dhcpv4_add_file(struct net_pkt *pkt)
133 {
134 if (net_pkt_memset(pkt, 0, SIZE_OF_FILE)) {
135 return false;
136 }
137
138 return true;
139 }
140
141 /* SNAME is empty ATM */
dhcpv4_add_sname(struct net_pkt * pkt)142 static inline bool dhcpv4_add_sname(struct net_pkt *pkt)
143 {
144 if (net_pkt_memset(pkt, 0, SIZE_OF_SNAME)) {
145 return false;
146 }
147
148 return true;
149 }
150
151 /* Create DHCPv4 message and add options as per message type */
dhcpv4_create_message(struct net_if * iface,uint8_t type,const struct in_addr * ciaddr,const struct in_addr * src_addr,const struct in_addr * server_addr,bool server_id,bool requested_ip)152 static struct net_pkt *dhcpv4_create_message(struct net_if *iface, uint8_t type,
153 const struct in_addr *ciaddr,
154 const struct in_addr *src_addr,
155 const struct in_addr *server_addr,
156 bool server_id, bool requested_ip)
157 {
158 NET_PKT_DATA_ACCESS_DEFINE(dhcp_access, struct dhcp_msg);
159 const struct in_addr *addr;
160 size_t size = DHCPV4_MESSAGE_SIZE;
161 struct net_pkt *pkt;
162 struct dhcp_msg *msg;
163 #if defined(CONFIG_NET_HOSTNAME_ENABLE)
164 const char *hostname = net_hostname_get();
165 const size_t hostname_size = strlen(hostname);
166 #endif
167
168 if (src_addr == NULL) {
169 addr = net_ipv4_unspecified_address();
170 } else {
171 addr = src_addr;
172 }
173
174 if (server_id) {
175 size += DHCPV4_OLV_MSG_SERVER_ID;
176 }
177
178 if (requested_ip) {
179 size += DHCPV4_OLV_MSG_REQ_IPADDR;
180 }
181
182 if (type == NET_DHCPV4_MSG_TYPE_DISCOVER) {
183 size += DHCPV4_OLV_MSG_REQ_LIST;
184 }
185
186 #if defined(CONFIG_NET_HOSTNAME_ENABLE)
187 if (hostname_size > 0) {
188 size += DHCPV4_OLV_MSG_HOST_NAME + hostname_size;
189 }
190 #endif
191
192 pkt = net_pkt_alloc_with_buffer(iface, size, AF_INET,
193 IPPROTO_UDP, K_FOREVER);
194 if (!pkt) {
195 return NULL;
196 }
197
198 net_pkt_set_ipv4_ttl(pkt, 0xFF);
199
200 if (net_ipv4_create(pkt, addr, server_addr) ||
201 net_udp_create(pkt, htons(DHCPV4_CLIENT_PORT),
202 htons(DHCPV4_SERVER_PORT))) {
203 goto fail;
204 }
205
206 msg = (struct dhcp_msg *)net_pkt_get_data(pkt, &dhcp_access);
207
208 (void)memset(msg, 0, sizeof(struct dhcp_msg));
209
210 msg->op = DHCPV4_MSG_BOOT_REQUEST;
211 msg->htype = HARDWARE_ETHERNET_TYPE;
212 msg->hlen = net_if_get_link_addr(iface)->len;
213 msg->xid = htonl(iface->config.dhcpv4.xid);
214 msg->flags = IS_ENABLED(CONFIG_NET_DHCPV4_ACCEPT_UNICAST) ?
215 htons(DHCPV4_MSG_UNICAST) : htons(DHCPV4_MSG_BROADCAST);
216
217 if (ciaddr) {
218 /* The ciaddr field was zero'd out above, if we are
219 * asked to send a ciaddr then fill it in now
220 * otherwise leave it as all zeros.
221 */
222 memcpy(msg->ciaddr, ciaddr, 4);
223 }
224
225 memcpy(msg->chaddr, net_if_get_link_addr(iface)->addr,
226 net_if_get_link_addr(iface)->len);
227
228 if (net_pkt_set_data(pkt, &dhcp_access)) {
229 goto fail;
230 }
231
232 if (!dhcpv4_add_sname(pkt) ||
233 !dhcpv4_add_file(pkt) ||
234 !dhcpv4_add_cookie(pkt) ||
235 !dhcpv4_add_msg_type(pkt, type)) {
236 goto fail;
237 }
238
239 if ((server_id &&
240 !dhcpv4_add_server_id(pkt, &iface->config.dhcpv4.server_id)) ||
241 (requested_ip &&
242 !dhcpv4_add_req_ipaddr(pkt, &iface->config.dhcpv4.requested_ip))) {
243 goto fail;
244 }
245
246 if (type == NET_DHCPV4_MSG_TYPE_DISCOVER && !dhcpv4_add_req_options(pkt)) {
247 goto fail;
248 }
249
250 #if defined(CONFIG_NET_HOSTNAME_ENABLE)
251 if (hostname_size > 0 &&
252 !dhcpv4_add_hostname(pkt, hostname, hostname_size)) {
253 goto fail;
254 }
255 #endif
256
257 if (!dhcpv4_add_end(pkt)) {
258 goto fail;
259 }
260
261 net_pkt_cursor_init(pkt);
262
263 net_ipv4_finalize(pkt, IPPROTO_UDP);
264
265 return pkt;
266
267 fail:
268 NET_DBG("Message creation failed");
269 net_pkt_unref(pkt);
270
271 return NULL;
272 }
273
274 /* Must be invoked with lock held. */
dhcpv4_immediate_timeout(struct net_if_dhcpv4 * dhcpv4)275 static void dhcpv4_immediate_timeout(struct net_if_dhcpv4 *dhcpv4)
276 {
277 NET_DBG("force timeout dhcpv4=%p", dhcpv4);
278 dhcpv4->timer_start = k_uptime_get() - 1;
279 dhcpv4->request_time = 0U;
280 k_work_reschedule(&timeout_work, K_NO_WAIT);
281 }
282
283 /* Must be invoked with lock held. */
dhcpv4_set_timeout(struct net_if_dhcpv4 * dhcpv4,uint32_t timeout)284 static void dhcpv4_set_timeout(struct net_if_dhcpv4 *dhcpv4,
285 uint32_t timeout)
286 {
287 NET_DBG("sched timeout dhcvp4=%p timeout=%us", dhcpv4, timeout);
288 dhcpv4->timer_start = k_uptime_get();
289 dhcpv4->request_time = timeout;
290
291 /* NB: This interface may not be providing the next timeout
292 * event; also this timeout may replace the current timeout
293 * event. Delegate scheduling to the timeout manager.
294 */
295 k_work_reschedule(&timeout_work, K_NO_WAIT);
296 }
297
298 /* Must be invoked with lock held */
dhcpv4_update_message_timeout(struct net_if_dhcpv4 * dhcpv4)299 static uint32_t dhcpv4_update_message_timeout(struct net_if_dhcpv4 *dhcpv4)
300 {
301 uint32_t timeout;
302
303 timeout = DHCPV4_INITIAL_RETRY_TIMEOUT << dhcpv4->attempts;
304
305 /* Max 64 seconds, see RFC 2131 chapter 4.1 */
306 if (timeout < DHCPV4_INITIAL_RETRY_TIMEOUT || timeout > 64) {
307 timeout = 64;
308 }
309
310 /* +1/-1 second randomization */
311 timeout += (sys_rand32_get() % 3U) - 1;
312
313 dhcpv4->attempts++;
314 dhcpv4_set_timeout(dhcpv4, timeout);
315
316 return timeout;
317 }
318
319 /* Prepare DHCPv4 Message request and send it to peer.
320 *
321 * Returns the number of seconds until the next time-triggered event,
322 * or UINT32_MAX if the client is in an invalid state.
323 */
dhcpv4_send_request(struct net_if * iface)324 static uint32_t dhcpv4_send_request(struct net_if *iface)
325 {
326 const struct in_addr *server_addr = net_ipv4_broadcast_address();
327 const struct in_addr *ciaddr = NULL;
328 const struct in_addr *src_addr = NULL;
329 bool with_server_id = false;
330 bool with_requested_ip = false;
331 struct net_pkt *pkt = NULL;
332 uint32_t timeout = UINT32_MAX;
333
334 iface->config.dhcpv4.xid++;
335
336 switch (iface->config.dhcpv4.state) {
337 case NET_DHCPV4_DISABLED:
338 case NET_DHCPV4_INIT:
339 case NET_DHCPV4_SELECTING:
340 case NET_DHCPV4_BOUND:
341 /* Not possible */
342 NET_ASSERT(0, "Invalid state %s",
343 net_dhcpv4_state_name(iface->config.dhcpv4.state));
344 goto fail;
345 break;
346 case NET_DHCPV4_REQUESTING:
347 with_server_id = true;
348 with_requested_ip = true;
349 memcpy(&iface->config.dhcpv4.request_server_addr, &iface->config.dhcpv4.server_id,
350 sizeof(struct in_addr));
351 break;
352 case NET_DHCPV4_RENEWING:
353 /* Since we have an address populate the ciaddr field.
354 */
355 ciaddr = &iface->config.dhcpv4.requested_ip;
356
357 /* UNICAST the DHCPREQUEST */
358 src_addr = ciaddr;
359 server_addr = &iface->config.dhcpv4.server_id;
360
361 /* RFC2131 4.4.5 Client MUST NOT include server
362 * identifier in the DHCPREQUEST.
363 */
364 break;
365 case NET_DHCPV4_REBINDING:
366 /* Since we have an address populate the ciaddr field.
367 */
368 ciaddr = &iface->config.dhcpv4.requested_ip;
369 src_addr = ciaddr;
370
371 break;
372 }
373
374 timeout = dhcpv4_update_message_timeout(&iface->config.dhcpv4);
375
376 pkt = dhcpv4_create_message(iface, NET_DHCPV4_MSG_TYPE_REQUEST,
377 ciaddr, src_addr, server_addr,
378 with_server_id, with_requested_ip);
379 if (!pkt) {
380 goto fail;
381 }
382
383 if (net_send_data(pkt) < 0) {
384 goto fail;
385 }
386
387 net_stats_update_udp_sent(iface);
388
389 NET_DBG("send request dst=%s xid=0x%x ciaddr=%s%s%s timeout=%us",
390 net_sprint_ipv4_addr(server_addr),
391 iface->config.dhcpv4.xid,
392 ciaddr ?
393 net_sprint_ipv4_addr(ciaddr) : "<unknown>",
394 with_server_id ? " +server-id" : "",
395 with_requested_ip ? " +requested-ip" : "",
396 timeout);
397
398 return timeout;
399
400 fail:
401 if (pkt) {
402 net_pkt_unref(pkt);
403 }
404
405 return timeout;
406 }
407
408 /* Prepare DHCPv4 Discover message and broadcast it */
dhcpv4_send_discover(struct net_if * iface)409 static uint32_t dhcpv4_send_discover(struct net_if *iface)
410 {
411 struct net_pkt *pkt;
412 uint32_t timeout;
413
414 iface->config.dhcpv4.xid++;
415
416 pkt = dhcpv4_create_message(iface, NET_DHCPV4_MSG_TYPE_DISCOVER,
417 NULL, NULL, net_ipv4_broadcast_address(),
418 false, false);
419 if (!pkt) {
420 goto fail;
421 }
422
423 if (net_send_data(pkt) < 0) {
424 goto fail;
425 }
426
427 net_stats_update_udp_sent(iface);
428
429 timeout = dhcpv4_update_message_timeout(&iface->config.dhcpv4);
430
431 NET_DBG("send discover xid=0x%x timeout=%us",
432 iface->config.dhcpv4.xid, timeout);
433
434 return timeout;
435
436 fail:
437 if (pkt) {
438 net_pkt_unref(pkt);
439 }
440
441 return iface->config.dhcpv4.xid %
442 (CONFIG_NET_DHCPV4_INITIAL_DELAY_MAX -
443 DHCPV4_INITIAL_DELAY_MIN) +
444 DHCPV4_INITIAL_DELAY_MIN;
445 }
446
dhcpv4_enter_selecting(struct net_if * iface)447 static void dhcpv4_enter_selecting(struct net_if *iface)
448 {
449 iface->config.dhcpv4.attempts = 0U;
450
451 iface->config.dhcpv4.lease_time = 0U;
452 iface->config.dhcpv4.renewal_time = 0U;
453 iface->config.dhcpv4.rebinding_time = 0U;
454
455 iface->config.dhcpv4.server_id.s_addr = INADDR_ANY;
456 iface->config.dhcpv4.requested_ip.s_addr = INADDR_ANY;
457
458 iface->config.dhcpv4.state = NET_DHCPV4_SELECTING;
459 NET_DBG("enter state=%s",
460 net_dhcpv4_state_name(iface->config.dhcpv4.state));
461 }
462
dhcpv4_get_timeleft(int64_t start,uint32_t time,int64_t now)463 static uint32_t dhcpv4_get_timeleft(int64_t start, uint32_t time, int64_t now)
464 {
465 int64_t deadline = start + MSEC_PER_SEC * time;
466 uint32_t ret = 0U;
467
468 /* If we haven't reached the deadline, calculate the
469 * rounded-up whole seconds until the deadline.
470 */
471 if (deadline > now) {
472 ret = (uint32_t)DIV_ROUND_UP(deadline - now, MSEC_PER_SEC);
473 }
474
475 return ret;
476 }
477
dhcpv4_request_timeleft(struct net_if * iface,int64_t now)478 static uint32_t dhcpv4_request_timeleft(struct net_if *iface, int64_t now)
479 {
480 uint32_t request_time = iface->config.dhcpv4.request_time;
481
482 return dhcpv4_get_timeleft(iface->config.dhcpv4.timer_start,
483 request_time, now);
484 }
485
dhcpv4_renewal_timeleft(struct net_if * iface,int64_t now)486 static uint32_t dhcpv4_renewal_timeleft(struct net_if *iface, int64_t now)
487 {
488 uint32_t rem = dhcpv4_get_timeleft(iface->config.dhcpv4.timer_start,
489 iface->config.dhcpv4.renewal_time,
490 now);
491
492 if (rem == 0U) {
493 iface->config.dhcpv4.state = NET_DHCPV4_RENEWING;
494 NET_DBG("enter state=%s",
495 net_dhcpv4_state_name(iface->config.dhcpv4.state));
496 iface->config.dhcpv4.attempts = 0U;
497 }
498
499 return rem;
500 }
501
dhcpv4_rebinding_timeleft(struct net_if * iface,int64_t now)502 static uint32_t dhcpv4_rebinding_timeleft(struct net_if *iface, int64_t now)
503 {
504 uint32_t rem = dhcpv4_get_timeleft(iface->config.dhcpv4.timer_start,
505 iface->config.dhcpv4.rebinding_time,
506 now);
507 if (rem == 0U) {
508 iface->config.dhcpv4.state = NET_DHCPV4_REBINDING;
509 NET_DBG("enter state=%s",
510 net_dhcpv4_state_name(iface->config.dhcpv4.state));
511 iface->config.dhcpv4.attempts = 0U;
512 }
513
514 return rem;
515 }
516
dhcpv4_enter_requesting(struct net_if * iface,struct dhcp_msg * msg)517 static void dhcpv4_enter_requesting(struct net_if *iface, struct dhcp_msg *msg)
518 {
519 iface->config.dhcpv4.attempts = 0U;
520 iface->config.dhcpv4.state = NET_DHCPV4_REQUESTING;
521 NET_DBG("enter state=%s",
522 net_dhcpv4_state_name(iface->config.dhcpv4.state));
523
524 memcpy(iface->config.dhcpv4.requested_ip.s4_addr,
525 msg->yiaddr, sizeof(msg->yiaddr));
526
527 dhcpv4_send_request(iface);
528 }
529
530 /* Must be invoked with lock held */
dhcpv4_enter_bound(struct net_if * iface)531 static void dhcpv4_enter_bound(struct net_if *iface)
532 {
533 uint32_t renewal_time;
534 uint32_t rebinding_time;
535
536 renewal_time = iface->config.dhcpv4.renewal_time;
537 if (!renewal_time) {
538 /* The default renewal time rfc2131 4.4.5 */
539 renewal_time = iface->config.dhcpv4.lease_time / 2U;
540 iface->config.dhcpv4.renewal_time = renewal_time;
541 }
542
543 rebinding_time = iface->config.dhcpv4.rebinding_time;
544 if (!rebinding_time) {
545 /* The default rebinding time rfc2131 4.4.5 */
546 rebinding_time = iface->config.dhcpv4.lease_time * 875U / 1000;
547 iface->config.dhcpv4.rebinding_time = rebinding_time;
548 }
549
550 iface->config.dhcpv4.state = NET_DHCPV4_BOUND;
551 NET_DBG("enter state=%s renewal=%us rebinding=%us",
552 net_dhcpv4_state_name(iface->config.dhcpv4.state),
553 renewal_time, rebinding_time);
554
555 dhcpv4_set_timeout(&iface->config.dhcpv4,
556 MIN(renewal_time, rebinding_time));
557
558 net_mgmt_event_notify_with_info(NET_EVENT_IPV4_DHCP_BOUND, iface,
559 &iface->config.dhcpv4,
560 sizeof(iface->config.dhcpv4));
561 }
562
dhcpv4_manage_timers(struct net_if * iface,int64_t now)563 static uint32_t dhcpv4_manage_timers(struct net_if *iface, int64_t now)
564 {
565 uint32_t timeleft = dhcpv4_request_timeleft(iface, now);
566
567 NET_DBG("iface %p dhcpv4=%p state=%s timeleft=%u", iface,
568 &iface->config.dhcpv4,
569 net_dhcpv4_state_name(iface->config.dhcpv4.state), timeleft);
570
571 if (timeleft != 0U) {
572 return timeleft;
573 }
574
575 if (!net_if_is_up(iface)) {
576 /* An interface is down, the registered event handler will
577 * restart DHCP procedure when the interface is back up.
578 */
579 return UINT32_MAX;
580 }
581
582 switch (iface->config.dhcpv4.state) {
583 case NET_DHCPV4_DISABLED:
584 break;
585 case NET_DHCPV4_INIT:
586 dhcpv4_enter_selecting(iface);
587 __fallthrough;
588 case NET_DHCPV4_SELECTING:
589 /* Failed to get OFFER message, send DISCOVER again */
590 return dhcpv4_send_discover(iface);
591 case NET_DHCPV4_REQUESTING:
592 /* Maximum number of renewal attempts failed, so start
593 * from the beginning.
594 */
595 if (iface->config.dhcpv4.attempts >=
596 DHCPV4_MAX_NUMBER_OF_ATTEMPTS) {
597 NET_DBG("too many attempts, restart");
598 dhcpv4_enter_selecting(iface);
599 return dhcpv4_send_discover(iface);
600 }
601
602 return dhcpv4_send_request(iface);
603 case NET_DHCPV4_BOUND:
604 timeleft = dhcpv4_renewal_timeleft(iface, now);
605 if (timeleft != 0U) {
606 timeleft = MIN(timeleft,
607 dhcpv4_rebinding_timeleft(iface, now));
608 }
609 if (timeleft == 0U) {
610 return dhcpv4_send_request(iface);
611 }
612
613 return timeleft;
614 case NET_DHCPV4_RENEWING:
615 case NET_DHCPV4_REBINDING:
616 if (iface->config.dhcpv4.attempts >=
617 DHCPV4_MAX_NUMBER_OF_ATTEMPTS) {
618 NET_DBG("too many attempts, restart");
619
620 if (!net_if_ipv4_addr_rm(iface,
621 &iface->config.dhcpv4.requested_ip)) {
622 NET_DBG("Failed to remove addr from iface");
623 }
624
625 /* Maximum number of renewal attempts failed, so start
626 * from the beginning.
627 */
628 dhcpv4_enter_selecting(iface);
629 return dhcpv4_send_discover(iface);
630 } else {
631 return dhcpv4_send_request(iface);
632 }
633 }
634
635 return UINT32_MAX;
636 }
637
dhcpv4_timeout(struct k_work * work)638 static void dhcpv4_timeout(struct k_work *work)
639 {
640 uint32_t timeout_update = UINT32_MAX;
641 int64_t now = k_uptime_get();
642 struct net_if_dhcpv4 *current, *next;
643
644 ARG_UNUSED(work);
645
646 k_mutex_lock(&lock, K_FOREVER);
647
648 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&dhcpv4_ifaces, current, next, node) {
649 struct net_if *iface = CONTAINER_OF(
650 CONTAINER_OF(current, struct net_if_config, dhcpv4),
651 struct net_if, config);
652 uint32_t next_timeout;
653
654 next_timeout = dhcpv4_manage_timers(iface, now);
655 if (next_timeout < timeout_update) {
656 timeout_update = next_timeout;
657 }
658 }
659
660 k_mutex_unlock(&lock);
661
662 if (timeout_update != UINT32_MAX) {
663 NET_DBG("Waiting for %us", timeout_update);
664
665 k_work_reschedule(&timeout_work,
666 K_SECONDS(timeout_update));
667 }
668 }
669
670 /* Parse DHCPv4 options and retrieve relevant information
671 * as per RFC 2132.
672 */
dhcpv4_parse_options(struct net_pkt * pkt,struct net_if * iface,enum net_dhcpv4_msg_type * msg_type)673 static bool dhcpv4_parse_options(struct net_pkt *pkt,
674 struct net_if *iface,
675 enum net_dhcpv4_msg_type *msg_type)
676 {
677 #if defined(CONFIG_NET_DHCPV4_OPTION_CALLBACKS)
678 struct net_dhcpv4_option_callback *cb, *tmp;
679 struct net_pkt_cursor backup;
680 #endif
681 uint8_t cookie[4];
682 uint8_t length;
683 uint8_t type;
684 bool router_present = false;
685 bool unhandled = true;
686
687 if (net_pkt_read(pkt, cookie, sizeof(cookie)) ||
688 memcmp(magic_cookie, cookie, sizeof(magic_cookie))) {
689 NET_DBG("Incorrect magic cookie");
690 return false;
691 }
692
693 while (!net_pkt_read_u8(pkt, &type)) {
694 if (type == DHCPV4_OPTIONS_END) {
695 NET_DBG("options_end");
696 goto end;
697 }
698
699 if (net_pkt_read_u8(pkt, &length)) {
700 NET_ERR("option parsing, bad length");
701 return false;
702 }
703
704
705 #if defined(CONFIG_NET_DHCPV4_OPTION_CALLBACKS)
706 net_pkt_cursor_backup(pkt, &backup);
707 unhandled = true;
708
709 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&option_callbacks,
710 cb, tmp, node) {
711 if (cb->option == type) {
712 NET_ASSERT(cb->handler, "No callback handler!");
713
714 if (net_pkt_read(pkt, cb->data,
715 MIN(cb->max_length, length))) {
716 NET_DBG("option callback, read err");
717 return false;
718 }
719
720 cb->handler(cb, length, *msg_type, iface);
721 unhandled = false;
722 }
723 net_pkt_cursor_restore(pkt, &backup);
724 }
725 #endif /* CONFIG_NET_DHCPV4_OPTION_CALLBACKS */
726
727 switch (type) {
728 case DHCPV4_OPTIONS_SUBNET_MASK: {
729 struct in_addr netmask;
730
731 if (length != 4U) {
732 NET_ERR("options_subnet_mask, bad length");
733 return false;
734 }
735
736 if (net_pkt_read(pkt, netmask.s4_addr, length)) {
737 NET_ERR("options_subnet_mask, short packet");
738 return false;
739 }
740
741 net_if_ipv4_set_netmask(iface, &netmask);
742 NET_DBG("options_subnet_mask %s",
743 net_sprint_ipv4_addr(&netmask));
744 break;
745 }
746 case DHCPV4_OPTIONS_ROUTER: {
747 struct in_addr router;
748
749 /* Router option may present 1 or more
750 * addresses for routers on the clients
751 * subnet. Routers should be listed in order
752 * of preference. Hence we choose the first
753 * and skip the rest.
754 */
755 if (length % 4 != 0U || length < 4) {
756 NET_ERR("options_router, bad length");
757 return false;
758 }
759
760 if (net_pkt_read(pkt, router.s4_addr, 4) ||
761 net_pkt_skip(pkt, length - 4U)) {
762 NET_ERR("options_router, short packet");
763 return false;
764 }
765
766 NET_DBG("options_router: %s",
767 net_sprint_ipv4_addr(&router));
768 net_if_ipv4_set_gw(iface, &router);
769 router_present = true;
770
771 break;
772 }
773 #if defined(CONFIG_DNS_RESOLVER)
774 case DHCPV4_OPTIONS_DNS_SERVER: {
775 struct dns_resolve_context *ctx;
776 struct sockaddr_in dns;
777 const struct sockaddr *dns_servers[] = {
778 (struct sockaddr *)&dns, NULL
779 };
780 int status;
781
782 /* DNS server option may present 1 or more
783 * addresses. Each 4 bytes in length. DNS
784 * servers should be listed in order
785 * of preference. Hence we choose the first
786 * and skip the rest.
787 */
788 if (length % 4 != 0U) {
789 NET_ERR("options_dns, bad length");
790 return false;
791 }
792
793 (void)memset(&dns, 0, sizeof(dns));
794
795 if (net_pkt_read(pkt, dns.sin_addr.s4_addr, 4) ||
796 net_pkt_skip(pkt, length - 4U)) {
797 NET_ERR("options_dns, short packet");
798 return false;
799 }
800
801 ctx = dns_resolve_get_default();
802 dns.sin_family = AF_INET;
803 status = dns_resolve_reconfigure(ctx, NULL, dns_servers);
804 if (status < 0) {
805 NET_DBG("options_dns, failed to set "
806 "resolve address: %d", status);
807 return false;
808 }
809
810 break;
811 }
812 #endif
813 case DHCPV4_OPTIONS_LEASE_TIME:
814 if (length != 4U) {
815 NET_ERR("options_lease_time, bad length");
816 return false;
817 }
818
819 if (net_pkt_read_be32(
820 pkt, &iface->config.dhcpv4.lease_time) ||
821 !iface->config.dhcpv4.lease_time) {
822 NET_ERR("options_lease_time, wrong value");
823 return false;
824 }
825
826 NET_DBG("options_lease_time: %u",
827 iface->config.dhcpv4.lease_time);
828
829 break;
830 case DHCPV4_OPTIONS_RENEWAL:
831 if (length != 4U) {
832 NET_DBG("options_renewal, bad length");
833 return false;
834 }
835
836 if (net_pkt_read_be32(
837 pkt, &iface->config.dhcpv4.renewal_time) ||
838 !iface->config.dhcpv4.renewal_time) {
839 NET_DBG("options_renewal, wrong value");
840 return false;
841 }
842
843 NET_DBG("options_renewal: %u",
844 iface->config.dhcpv4.renewal_time);
845
846 break;
847 case DHCPV4_OPTIONS_REBINDING:
848 if (length != 4U) {
849 NET_DBG("options_rebinding, bad length");
850 return false;
851 }
852
853 if (net_pkt_read_be32(
854 pkt,
855 &iface->config.dhcpv4.rebinding_time) ||
856 !iface->config.dhcpv4.rebinding_time) {
857 NET_DBG("options_rebinding, wrong value");
858 return false;
859 }
860
861 NET_DBG("options_rebinding: %u",
862 iface->config.dhcpv4.rebinding_time);
863
864 break;
865 case DHCPV4_OPTIONS_SERVER_ID:
866 if (length != 4U) {
867 NET_DBG("options_server_id, bad length");
868 return false;
869 }
870
871 if (net_pkt_read(
872 pkt,
873 iface->config.dhcpv4.server_id.s4_addr,
874 length)) {
875 NET_DBG("options_server_id, read err");
876 return false;
877 }
878
879 NET_DBG("options_server_id: %s",
880 net_sprint_ipv4_addr(&iface->config.dhcpv4.server_id));
881 break;
882 case DHCPV4_OPTIONS_MSG_TYPE: {
883 if (length != 1U) {
884 NET_DBG("options_msg_type, bad length");
885 return false;
886 }
887
888 {
889 uint8_t val = 0U;
890
891 if (net_pkt_read_u8(pkt, &val)) {
892 NET_DBG("options_msg_type, read err");
893 return false;
894 }
895 *msg_type = val;
896 }
897
898 break;
899 }
900 default:
901 if (unhandled) {
902 NET_DBG("option unknown: %d", type);
903 } else {
904 NET_DBG("option unknown, handled by callback: %d", type);
905 }
906
907 if (net_pkt_skip(pkt, length)) {
908 NET_DBG("option unknown, skip err");
909 return false;
910 }
911 break;
912 }
913 }
914
915 /* Invalid case: Options without DHCPV4_OPTIONS_END. */
916 return false;
917
918 end:
919 if (*msg_type == NET_DHCPV4_MSG_TYPE_OFFER && !router_present) {
920 struct in_addr any = INADDR_ANY_INIT;
921
922 net_if_ipv4_set_gw(iface, &any);
923 }
924
925 return true;
926 }
927
dhcpv4_handle_msg_offer(struct net_if * iface,struct dhcp_msg * msg)928 static inline void dhcpv4_handle_msg_offer(struct net_if *iface,
929 struct dhcp_msg *msg)
930 {
931 switch (iface->config.dhcpv4.state) {
932 case NET_DHCPV4_DISABLED:
933 case NET_DHCPV4_INIT:
934 case NET_DHCPV4_REQUESTING:
935 case NET_DHCPV4_RENEWING:
936 case NET_DHCPV4_REBINDING:
937 case NET_DHCPV4_BOUND:
938 break;
939 case NET_DHCPV4_SELECTING:
940 dhcpv4_enter_requesting(iface, msg);
941 break;
942 }
943 }
944
945 /* Must be invoked with lock held */
dhcpv4_handle_msg_ack(struct net_if * iface)946 static void dhcpv4_handle_msg_ack(struct net_if *iface)
947 {
948 switch (iface->config.dhcpv4.state) {
949 case NET_DHCPV4_DISABLED:
950 case NET_DHCPV4_INIT:
951 case NET_DHCPV4_SELECTING:
952 case NET_DHCPV4_BOUND:
953 break;
954 case NET_DHCPV4_REQUESTING:
955 NET_INFO("Received: %s",
956 net_sprint_ipv4_addr(&iface->config.dhcpv4.requested_ip));
957
958 if (!net_if_ipv4_addr_add(iface,
959 &iface->config.dhcpv4.requested_ip,
960 NET_ADDR_DHCP,
961 iface->config.dhcpv4.lease_time)) {
962 NET_DBG("Failed to add IPv4 addr to iface %p", iface);
963 return;
964 }
965
966 dhcpv4_enter_bound(iface);
967 break;
968
969 case NET_DHCPV4_RENEWING:
970 case NET_DHCPV4_REBINDING:
971 /* TODO: If the renewal is success, update only
972 * vlifetime on iface.
973 */
974 dhcpv4_enter_bound(iface);
975 break;
976 }
977 }
978
dhcpv4_handle_msg_nak(struct net_if * iface)979 static void dhcpv4_handle_msg_nak(struct net_if *iface)
980 {
981 switch (iface->config.dhcpv4.state) {
982 case NET_DHCPV4_DISABLED:
983 case NET_DHCPV4_INIT:
984 case NET_DHCPV4_SELECTING:
985 case NET_DHCPV4_REQUESTING:
986 if (memcmp(&iface->config.dhcpv4.request_server_addr,
987 &iface->config.dhcpv4.response_src_addr,
988 sizeof(iface->config.dhcpv4.request_server_addr)) == 0) {
989 LOG_DBG("NAK from requesting server %s, restart config",
990 net_sprint_ipv4_addr(&iface->config.dhcpv4.request_server_addr));
991 dhcpv4_enter_selecting(iface);
992 } else {
993 LOG_DBG("NAK from non-requesting server %s, ignore it",
994 net_sprint_ipv4_addr(&iface->config.dhcpv4.response_src_addr));
995 }
996 break;
997 case NET_DHCPV4_BOUND:
998 break;
999 case NET_DHCPV4_RENEWING:
1000 case NET_DHCPV4_REBINDING:
1001 if (!net_if_ipv4_addr_rm(iface,
1002 &iface->config.dhcpv4.requested_ip)) {
1003 NET_DBG("Failed to remove addr from iface");
1004 }
1005
1006 /* Restart the configuration process. */
1007 dhcpv4_enter_selecting(iface);
1008 break;
1009 }
1010 }
1011
1012 /* Takes and releases lock */
dhcpv4_handle_reply(struct net_if * iface,enum net_dhcpv4_msg_type msg_type,struct dhcp_msg * msg)1013 static void dhcpv4_handle_reply(struct net_if *iface,
1014 enum net_dhcpv4_msg_type msg_type,
1015 struct dhcp_msg *msg)
1016 {
1017 NET_DBG("state=%s msg=%s",
1018 net_dhcpv4_state_name(iface->config.dhcpv4.state),
1019 net_dhcpv4_msg_type_name(msg_type));
1020
1021 switch (msg_type) {
1022 case NET_DHCPV4_MSG_TYPE_OFFER:
1023 dhcpv4_handle_msg_offer(iface, msg);
1024 break;
1025 case NET_DHCPV4_MSG_TYPE_ACK:
1026 dhcpv4_handle_msg_ack(iface);
1027 break;
1028 case NET_DHCPV4_MSG_TYPE_NAK:
1029 dhcpv4_handle_msg_nak(iface);
1030 break;
1031 default:
1032 NET_DBG("ignore message");
1033 break;
1034 }
1035 }
1036
net_dhcpv4_input(struct net_conn * conn,struct net_pkt * pkt,union net_ip_header * ip_hdr,union net_proto_header * proto_hdr,void * user_data)1037 static enum net_verdict net_dhcpv4_input(struct net_conn *conn,
1038 struct net_pkt *pkt,
1039 union net_ip_header *ip_hdr,
1040 union net_proto_header *proto_hdr,
1041 void *user_data)
1042 {
1043 NET_PKT_DATA_ACCESS_DEFINE(dhcp_access, struct dhcp_msg);
1044 enum net_verdict verdict = NET_DROP;
1045 enum net_dhcpv4_msg_type msg_type = 0;
1046 struct dhcp_msg *msg;
1047 struct net_if *iface;
1048
1049 if (!conn) {
1050 NET_DBG("Invalid connection");
1051 return NET_DROP;
1052 }
1053
1054 if (!pkt) {
1055 NET_DBG("Invalid packet");
1056 return NET_DROP;
1057 }
1058
1059 iface = net_pkt_iface(pkt);
1060 if (!iface) {
1061 NET_DBG("no iface");
1062 return NET_DROP;
1063 }
1064
1065 /* If the message is not DHCP then drop the packet. */
1066 if (net_pkt_get_len(pkt) < NET_IPV4UDPH_LEN + sizeof(struct dhcp_msg)) {
1067 NET_DBG("Input msg is not related to DHCPv4");
1068 return NET_DROP;
1069
1070 }
1071
1072 net_pkt_cursor_init(pkt);
1073
1074 if (net_pkt_skip(pkt, NET_IPV4UDPH_LEN)) {
1075 return NET_DROP;
1076 }
1077
1078 msg = (struct dhcp_msg *)net_pkt_get_data(pkt, &dhcp_access);
1079 if (!msg) {
1080 return NET_DROP;
1081 }
1082
1083 NET_DBG("Received dhcp msg [op=0x%x htype=0x%x hlen=%u xid=0x%x "
1084 "secs=%u flags=0x%x chaddr=%s",
1085 msg->op, msg->htype, msg->hlen, ntohl(msg->xid),
1086 msg->secs, msg->flags,
1087 net_sprint_ll_addr(msg->chaddr, 6));
1088 NET_DBG(" ciaddr=%d.%d.%d.%d",
1089 msg->ciaddr[0], msg->ciaddr[1], msg->ciaddr[2], msg->ciaddr[3]);
1090 NET_DBG(" yiaddr=%d.%d.%d.%d",
1091 msg->yiaddr[0], msg->yiaddr[1], msg->yiaddr[2], msg->yiaddr[3]);
1092 NET_DBG(" siaddr=%d.%d.%d.%d",
1093 msg->siaddr[0], msg->siaddr[1], msg->siaddr[2], msg->siaddr[3]);
1094 NET_DBG(" giaddr=%d.%d.%d.%d]",
1095 msg->giaddr[0], msg->giaddr[1], msg->giaddr[2], msg->giaddr[3]);
1096
1097 k_mutex_lock(&lock, K_FOREVER);
1098
1099 if (!(msg->op == DHCPV4_MSG_BOOT_REPLY &&
1100 iface->config.dhcpv4.xid == ntohl(msg->xid) &&
1101 !memcmp(msg->chaddr, net_if_get_link_addr(iface)->addr,
1102 net_if_get_link_addr(iface)->len))) {
1103
1104 NET_DBG("Unexpected op (%d), xid (%x vs %x) or chaddr",
1105 msg->op, iface->config.dhcpv4.xid, ntohl(msg->xid));
1106 goto drop;
1107 }
1108
1109 if (msg->hlen != net_if_get_link_addr(iface)->len) {
1110 NET_DBG("Unexpected hlen (%d)", msg->hlen);
1111 goto drop;
1112 }
1113
1114 net_pkt_acknowledge_data(pkt, &dhcp_access);
1115
1116 /* SNAME, FILE are not used at the moment, skip it */
1117 if (net_pkt_skip(pkt, SIZE_OF_SNAME + SIZE_OF_FILE)) {
1118 NET_DBG("short packet while skipping sname");
1119 goto drop;
1120 }
1121
1122 if (!dhcpv4_parse_options(pkt, iface, &msg_type)) {
1123 goto drop;
1124 }
1125
1126 memcpy(&iface->config.dhcpv4.response_src_addr, ip_hdr->ipv4->src,
1127 sizeof(struct in_addr));
1128
1129 dhcpv4_handle_reply(iface, msg_type, msg);
1130
1131 net_pkt_unref(pkt);
1132
1133 verdict = NET_OK;
1134
1135 drop:
1136 k_mutex_unlock(&lock);
1137
1138 return verdict;
1139 }
1140
dhcpv4_iface_event_handler(struct net_mgmt_event_callback * cb,uint32_t mgmt_event,struct net_if * iface)1141 static void dhcpv4_iface_event_handler(struct net_mgmt_event_callback *cb,
1142 uint32_t mgmt_event, struct net_if *iface)
1143 {
1144 sys_snode_t *node = NULL;
1145
1146 k_mutex_lock(&lock, K_FOREVER);
1147
1148 SYS_SLIST_FOR_EACH_NODE(&dhcpv4_ifaces, node) {
1149 if (node == &iface->config.dhcpv4.node) {
1150 break;
1151 }
1152 }
1153
1154 if (node == NULL) {
1155 goto out;
1156 }
1157
1158 if (mgmt_event == NET_EVENT_IF_DOWN) {
1159 NET_DBG("Interface %p going down", iface);
1160
1161 if (iface->config.dhcpv4.state == NET_DHCPV4_BOUND) {
1162 iface->config.dhcpv4.attempts = 0U;
1163 iface->config.dhcpv4.state = NET_DHCPV4_RENEWING;
1164 NET_DBG("enter state=%s", net_dhcpv4_state_name(
1165 iface->config.dhcpv4.state));
1166 }
1167 } else if (mgmt_event == NET_EVENT_IF_UP) {
1168 NET_DBG("Interface %p coming up", iface);
1169
1170 /* We should not call dhcpv4_send_request() directly here as
1171 * the CONFIG_NET_MGMT_EVENT_STACK_SIZE is not large
1172 * enough. Instead we can force a request timeout
1173 * which will then call dhcpv4_send_request() automatically.
1174 */
1175 dhcpv4_immediate_timeout(&iface->config.dhcpv4);
1176 }
1177
1178 out:
1179 k_mutex_unlock(&lock);
1180 }
1181
net_dhcpv4_state_name(enum net_dhcpv4_state state)1182 const char *net_dhcpv4_state_name(enum net_dhcpv4_state state)
1183 {
1184 static const char * const name[] = {
1185 "disabled",
1186 "init",
1187 "selecting",
1188 "requesting",
1189 "renewing",
1190 "rebinding",
1191 "bound",
1192 };
1193
1194 __ASSERT_NO_MSG(state >= 0 && state < sizeof(name));
1195 return name[state];
1196 }
1197
net_dhcpv4_msg_type_name(enum net_dhcpv4_msg_type msg_type)1198 const char *net_dhcpv4_msg_type_name(enum net_dhcpv4_msg_type msg_type)
1199 {
1200 static const char * const name[] = {
1201 "discover",
1202 "offer",
1203 "request",
1204 "decline",
1205 "ack",
1206 "nak",
1207 "release",
1208 "inform"
1209 };
1210
1211 __ASSERT_NO_MSG(msg_type >= 1 && msg_type <= sizeof(name));
1212 return name[msg_type - 1];
1213 }
1214
dhcpv4_start_internal(struct net_if * iface,bool first_start)1215 static void dhcpv4_start_internal(struct net_if *iface, bool first_start)
1216 {
1217 uint32_t entropy;
1218 uint32_t timeout = 0;
1219
1220 net_mgmt_event_notify(NET_EVENT_IPV4_DHCP_START, iface);
1221
1222 k_mutex_lock(&lock, K_FOREVER);
1223
1224 switch (iface->config.dhcpv4.state) {
1225 case NET_DHCPV4_DISABLED:
1226 iface->config.dhcpv4.state = NET_DHCPV4_INIT;
1227 NET_DBG("iface %p state=%s", iface,
1228 net_dhcpv4_state_name(iface->config.dhcpv4.state));
1229
1230 /* We need entropy for both an XID and a random delay
1231 * before sending the initial discover message.
1232 */
1233 entropy = sys_rand32_get();
1234
1235 /* A DHCP client MUST choose xid's in such a way as to
1236 * minimize the change of using and xid identical to
1237 * one used by another client. Choose a random xid st
1238 * startup and increment it on each new request.
1239 */
1240 iface->config.dhcpv4.xid = entropy;
1241
1242 /* Use default */
1243 if (first_start) {
1244 /* RFC2131 4.1.1 requires we wait a random period
1245 * between 1 and 10 seconds before sending the initial
1246 * discover.
1247 */
1248 timeout = entropy % (CONFIG_NET_DHCPV4_INITIAL_DELAY_MAX -
1249 DHCPV4_INITIAL_DELAY_MIN) + DHCPV4_INITIAL_DELAY_MIN;
1250 }
1251
1252 NET_DBG("wait timeout=%us", timeout);
1253
1254 if (sys_slist_is_empty(&dhcpv4_ifaces)) {
1255 net_mgmt_add_event_callback(&mgmt4_cb);
1256 }
1257
1258 sys_slist_append(&dhcpv4_ifaces,
1259 &iface->config.dhcpv4.node);
1260
1261 dhcpv4_set_timeout(&iface->config.dhcpv4, timeout);
1262
1263 break;
1264 case NET_DHCPV4_INIT:
1265 case NET_DHCPV4_SELECTING:
1266 case NET_DHCPV4_REQUESTING:
1267 case NET_DHCPV4_RENEWING:
1268 case NET_DHCPV4_REBINDING:
1269 case NET_DHCPV4_BOUND:
1270 break;
1271 }
1272
1273 k_mutex_unlock(&lock);
1274 }
1275
1276 #if defined(CONFIG_NET_DHCPV4_OPTION_CALLBACKS)
1277
net_dhcpv4_add_option_callback(struct net_dhcpv4_option_callback * cb)1278 int net_dhcpv4_add_option_callback(struct net_dhcpv4_option_callback *cb)
1279 {
1280 if (!cb || !cb->handler) {
1281 return -EINVAL;
1282 }
1283
1284 k_mutex_lock(&lock, K_FOREVER);
1285 sys_slist_prepend(&option_callbacks, &cb->node);
1286 k_mutex_unlock(&lock);
1287 return 0;
1288 }
1289
net_dhcpv4_remove_option_callback(struct net_dhcpv4_option_callback * cb)1290 int net_dhcpv4_remove_option_callback(struct net_dhcpv4_option_callback *cb)
1291 {
1292 int ret = 0;
1293
1294 if (!cb || !cb->handler) {
1295 return -EINVAL;
1296 }
1297
1298 k_mutex_lock(&lock, K_FOREVER);
1299 if (!sys_slist_find_and_remove(&option_callbacks, &cb->node)) {
1300 ret = -EINVAL;
1301 }
1302 k_mutex_unlock(&lock);
1303 return ret;
1304 }
1305
1306 #endif /* CONFIG_NET_DHCPV4_OPTION_CALLBACKS */
1307
net_dhcpv4_start(struct net_if * iface)1308 void net_dhcpv4_start(struct net_if *iface)
1309 {
1310 return dhcpv4_start_internal(iface, true);
1311 }
1312
net_dhcpv4_stop(struct net_if * iface)1313 void net_dhcpv4_stop(struct net_if *iface)
1314 {
1315 k_mutex_lock(&lock, K_FOREVER);
1316
1317 switch (iface->config.dhcpv4.state) {
1318 case NET_DHCPV4_DISABLED:
1319 break;
1320
1321 case NET_DHCPV4_RENEWING:
1322 case NET_DHCPV4_BOUND:
1323 if (!net_if_ipv4_addr_rm(iface,
1324 &iface->config.dhcpv4.requested_ip)) {
1325 NET_DBG("Failed to remove addr from iface");
1326 }
1327
1328 __fallthrough;
1329 case NET_DHCPV4_INIT:
1330 case NET_DHCPV4_SELECTING:
1331 case NET_DHCPV4_REQUESTING:
1332 case NET_DHCPV4_REBINDING:
1333 iface->config.dhcpv4.state = NET_DHCPV4_DISABLED;
1334 NET_DBG("state=%s",
1335 net_dhcpv4_state_name(iface->config.dhcpv4.state));
1336
1337 sys_slist_find_and_remove(&dhcpv4_ifaces,
1338 &iface->config.dhcpv4.node);
1339
1340 if (sys_slist_is_empty(&dhcpv4_ifaces)) {
1341 /* Best effort cancel. Handler is safe to invoke if
1342 * cancellation is unsuccessful.
1343 */
1344 (void)k_work_cancel_delayable(&timeout_work);
1345 net_mgmt_del_event_callback(&mgmt4_cb);
1346 }
1347
1348 break;
1349 }
1350
1351 net_mgmt_event_notify(NET_EVENT_IPV4_DHCP_STOP, iface);
1352
1353 k_mutex_unlock(&lock);
1354 }
1355
net_dhcpv4_restart(struct net_if * iface)1356 void net_dhcpv4_restart(struct net_if *iface)
1357 {
1358 net_dhcpv4_stop(iface);
1359 dhcpv4_start_internal(iface, false);
1360 }
1361
net_dhcpv4_init(void)1362 int net_dhcpv4_init(void)
1363 {
1364 struct sockaddr local_addr;
1365 int ret;
1366
1367 NET_DBG("");
1368
1369 net_ipaddr_copy(&net_sin(&local_addr)->sin_addr,
1370 net_ipv4_unspecified_address());
1371 local_addr.sa_family = AF_INET;
1372
1373 /* Register UDP input callback on
1374 * DHCPV4_SERVER_PORT(67) and DHCPV4_CLIENT_PORT(68) for
1375 * all dhcpv4 related incoming packets.
1376 */
1377 ret = net_udp_register(AF_INET, NULL, &local_addr,
1378 DHCPV4_SERVER_PORT,
1379 DHCPV4_CLIENT_PORT,
1380 NULL, net_dhcpv4_input, NULL, NULL);
1381 if (ret < 0) {
1382 NET_DBG("UDP callback registration failed");
1383 return ret;
1384 }
1385
1386 k_work_init_delayable(&timeout_work, dhcpv4_timeout);
1387
1388 /* Catch network interface UP or DOWN events and renew the address
1389 * if interface is coming back up again.
1390 */
1391 net_mgmt_init_event_callback(&mgmt4_cb, dhcpv4_iface_event_handler,
1392 NET_EVENT_IF_DOWN | NET_EVENT_IF_UP);
1393
1394 #if defined(CONFIG_NET_DHCPV4_OPTION_CALLBACKS)
1395 k_mutex_lock(&lock, K_FOREVER);
1396 sys_slist_init(&option_callbacks);
1397 k_mutex_unlock(&lock);
1398 #endif
1399 return 0;
1400 }
1401
1402 #if defined(CONFIG_NET_DHCPV4_ACCEPT_UNICAST)
net_dhcpv4_accept_unicast(struct net_pkt * pkt)1403 bool net_dhcpv4_accept_unicast(struct net_pkt *pkt)
1404 {
1405 NET_PKT_DATA_ACCESS_DEFINE(udp_access, struct net_udp_hdr);
1406 struct net_pkt_cursor backup;
1407 struct net_udp_hdr *udp_hdr;
1408 struct net_if *iface;
1409 bool accept = false;
1410
1411 iface = net_pkt_iface(pkt);
1412 if (iface == NULL) {
1413 return false;
1414 }
1415
1416 /* Only accept DHCPv4 packets during active query. */
1417 if (iface->config.dhcpv4.state != NET_DHCPV4_SELECTING &&
1418 iface->config.dhcpv4.state != NET_DHCPV4_REQUESTING &&
1419 iface->config.dhcpv4.state != NET_DHCPV4_RENEWING &&
1420 iface->config.dhcpv4.state != NET_DHCPV4_REBINDING) {
1421 return false;
1422 }
1423
1424 net_pkt_cursor_backup(pkt, &backup);
1425 net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt));
1426
1427 /* Verify destination UDP port. */
1428 udp_hdr = (struct net_udp_hdr *)net_pkt_get_data(pkt, &udp_access);
1429 if (udp_hdr == NULL) {
1430 goto out;
1431 }
1432
1433 if (udp_hdr->dst_port != htons(DHCPV4_CLIENT_PORT)) {
1434 goto out;
1435 }
1436
1437 accept = true;
1438
1439 out:
1440 net_pkt_cursor_restore(pkt, &backup);
1441
1442 return accept;
1443 }
1444 #endif /* CONFIG_NET_DHCPV4_ACCEPT_UNICAST */
1445