1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /** @file
8 * @brief DHCPv6 client implementation
9 */
10
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(net_dhcpv6, CONFIG_NET_DHCPV6_LOG_LEVEL);
13
14 #include <zephyr/net/dhcpv6.h>
15 #include <zephyr/net/net_ip.h>
16 #include <zephyr/random/random.h>
17 #include <zephyr/sys/math_extras.h>
18
19 #include "dhcpv6_internal.h"
20 #include "ipv6.h"
21 #include "net_private.h"
22 #include "udp_internal.h"
23
24 /* Maximum number of options client can request. */
25 #define DHCPV6_MAX_OPTION_REQUEST 2
26
27 struct dhcpv6_options_include {
28 bool clientid : 1;
29 bool serverid : 1;
30 bool elapsed_time : 1;
31 bool ia_na : 1;
32 bool iaaddr : 1;
33 bool ia_pd : 1;
34 bool iaprefix : 1;
35 uint16_t oro[DHCPV6_MAX_OPTION_REQUEST];
36 };
37
38 static K_MUTEX_DEFINE(lock);
39
40 /* All_DHCP_Relay_Agents_and_Servers (ff02::1:2) */
41 static const struct in6_addr all_dhcpv6_ra_and_servers = { { { 0xff, 0x02, 0, 0, 0, 0, 0, 0,
42 0, 0, 0, 0, 0, 0x01, 0, 0x02 } } };
43
44 static sys_slist_t dhcpv6_ifaces = SYS_SLIST_STATIC_INIT(&dhcpv6_ifaces);
45 static struct k_work_delayable dhcpv6_timeout_work;
46 static struct net_mgmt_event_callback dhcpv6_mgmt_cb;
47
net_dhcpv6_state_name(enum net_dhcpv6_state state)48 const char *net_dhcpv6_state_name(enum net_dhcpv6_state state)
49 {
50 static const char * const name[] = {
51 "disabled",
52 "init",
53 "soliciting",
54 "requesting",
55 "confirming",
56 "renewing",
57 "rebinding",
58 "information requesting",
59 "bound",
60 };
61
62 __ASSERT_NO_MSG(state >= 0 && state < sizeof(name));
63 return name[state];
64 }
65
dhcpv6_generate_tid(struct net_if * iface)66 static void dhcpv6_generate_tid(struct net_if *iface)
67 {
68 sys_rand_get(iface->config.dhcpv6.tid, sizeof(iface->config.dhcpv6.tid));
69 }
70
dhcvp6_update_deadlines(struct net_if * iface,int64_t now,uint32_t t1,uint32_t t2,uint32_t preferred_lifetime,uint32_t valid_lifetime)71 static void dhcvp6_update_deadlines(struct net_if *iface, int64_t now,
72 uint32_t t1, uint32_t t2,
73 uint32_t preferred_lifetime,
74 uint32_t valid_lifetime)
75 {
76 uint64_t t1_abs, t2_abs, expire_abs;
77
78 /* In case server does not set T1/T2 values, the time choice is left to
79 * the client discretion.
80 * Here, we use recommendations for the servers, where it's advised to
81 * set T1/T2 as 0.5 and 0.8 of the preferred lifetime.
82 */
83 if (t1 == 0 && t2 == 0) {
84 if (preferred_lifetime == DHCPV6_INFINITY) {
85 t1 = DHCPV6_INFINITY;
86 t2 = DHCPV6_INFINITY;
87 } else {
88 t1 = preferred_lifetime * 0.5;
89 t2 = preferred_lifetime * 0.8;
90 }
91 } else if (t1 == 0) {
92 if (t2 == DHCPV6_INFINITY) {
93 t1 = DHCPV6_INFINITY;
94 } else {
95 t1 = t2 * 0.625; /* 0.5 / 0.8 */
96 }
97 } else if (t2 == 0) {
98 if (t1 == DHCPV6_INFINITY) {
99 t2 = DHCPV6_INFINITY;
100 } else {
101 t2 = t1 * 1.6; /* 0.8 / 0.5 */
102 /* Overflow check. */
103 if (t2 < t1) {
104 t2 = DHCPV6_INFINITY;
105 }
106 }
107 } else if (t1 >= t2) {
108 NET_ERR("Invalid T1(%u)/T2(%u) values.", t1, t2);
109 return;
110 }
111
112 if (t1 == DHCPV6_INFINITY ||
113 u64_add_overflow(now, 1000ULL * t1, &t1_abs)) {
114 t1_abs = UINT64_MAX;
115 }
116
117 if (t2 == DHCPV6_INFINITY ||
118 u64_add_overflow(now, 1000ULL * t2, &t2_abs)) {
119 t2_abs = UINT64_MAX;
120 }
121
122 if (valid_lifetime == DHCPV6_INFINITY ||
123 u64_add_overflow(now, 1000ULL * valid_lifetime, &expire_abs)) {
124 expire_abs = UINT64_MAX;
125 }
126
127 if (iface->config.dhcpv6.t1 > t1_abs) {
128 iface->config.dhcpv6.t1 = t1_abs;
129 }
130
131 if (iface->config.dhcpv6.t2 > t2_abs) {
132 iface->config.dhcpv6.t2 = t2_abs;
133 }
134
135 if (iface->config.dhcpv6.expire < expire_abs) {
136 iface->config.dhcpv6.expire = expire_abs;
137 }
138 }
139
dhcpv6_set_timeout(struct net_if * iface,uint64_t timeout)140 static void dhcpv6_set_timeout(struct net_if *iface, uint64_t timeout)
141 {
142 int64_t now = k_uptime_get();
143
144 NET_DBG("sched dhcpv6 timeout iface=%p timeout=%llums", iface, timeout);
145
146 if (u64_add_overflow(now, timeout, &iface->config.dhcpv6.timeout)) {
147 iface->config.dhcpv6.timeout = UINT64_MAX;
148 }
149 }
150
dhcpv6_reschedule(void)151 static void dhcpv6_reschedule(void)
152 {
153 k_work_reschedule(&dhcpv6_timeout_work, K_NO_WAIT);
154 }
155
randomize_timeout(int multiplier,int timeout)156 static int randomize_timeout(int multiplier, int timeout)
157 {
158 int factor;
159
160 /* DHCPv6 RFC8415, ch. 15. the randomization factor should be a random
161 * number between -0.1 nand +0.1. As we operate on integers here, we
162 * scale it to -100 and +100, and divide the result by 1000.
163 */
164 factor = (int)(sys_rand32_get() % 201) - 100;
165
166 return (multiplier * timeout) + ((factor * timeout) / 1000);
167 }
168
dhcpv6_initial_retransmit_time(int init_retransmit_time)169 static int dhcpv6_initial_retransmit_time(int init_retransmit_time)
170 {
171 /* DHCPv6 RFC8415, ch. 15. Retransmission time for the first msg. */
172 return randomize_timeout(1, init_retransmit_time);
173 }
174
dhcpv6_next_retransmit_time(int prev_retransmit_time,int max_retransmit_time)175 static uint32_t dhcpv6_next_retransmit_time(int prev_retransmit_time,
176 int max_retransmit_time)
177 {
178 int retransmit_time;
179
180 /* DHCPv6 RFC8415, ch. 15. Retransmission time for the subsequent msg. */
181 retransmit_time = randomize_timeout(2, prev_retransmit_time);
182
183 if (max_retransmit_time == 0) {
184 return retransmit_time;
185 }
186
187 if (retransmit_time > max_retransmit_time) {
188 retransmit_time = randomize_timeout(1, max_retransmit_time);
189 }
190
191 return retransmit_time;
192 }
193
194 /* DHCPv6 packet encoding functions */
195
dhcpv6_add_header(struct net_pkt * pkt,enum dhcpv6_msg_type type,uint8_t * tid)196 static int dhcpv6_add_header(struct net_pkt *pkt, enum dhcpv6_msg_type type,
197 uint8_t *tid)
198 {
199 int ret;
200
201 ret = net_pkt_write_u8(pkt, type);
202 if (ret < 0) {
203 return ret;
204 }
205
206 ret = net_pkt_write(pkt, tid, DHCPV6_TID_SIZE);
207
208 return ret;
209 }
210
dhcpv6_add_option_header(struct net_pkt * pkt,enum dhcpv6_option_code code,uint16_t length)211 static int dhcpv6_add_option_header(struct net_pkt *pkt,
212 enum dhcpv6_option_code code,
213 uint16_t length)
214 {
215 int ret;
216
217 ret = net_pkt_write_be16(pkt, code);
218 if (ret < 0) {
219 return ret;
220 }
221
222 ret = net_pkt_write_be16(pkt, length);
223
224 return ret;
225 }
226
dhcpv6_add_option_clientid(struct net_pkt * pkt,struct net_dhcpv6_duid_storage * clientid)227 static int dhcpv6_add_option_clientid(struct net_pkt *pkt,
228 struct net_dhcpv6_duid_storage *clientid)
229 {
230 int ret;
231
232 ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_CLIENTID,
233 clientid->length);
234 if (ret < 0) {
235 return ret;
236 }
237
238 ret = net_pkt_write(pkt, &clientid->duid, clientid->length);
239
240 return ret;
241 }
242
dhcpv6_add_option_serverid(struct net_pkt * pkt,struct net_dhcpv6_duid_storage * serverid)243 static int dhcpv6_add_option_serverid(struct net_pkt *pkt,
244 struct net_dhcpv6_duid_storage *serverid)
245 {
246 int ret;
247
248 ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_SERVERID,
249 serverid->length);
250 if (ret < 0) {
251 return ret;
252 }
253
254 ret = net_pkt_write(pkt, &serverid->duid, serverid->length);
255
256 return ret;
257 }
258
259
dhcpv6_add_option_elapsed_time(struct net_pkt * pkt,uint64_t since)260 static int dhcpv6_add_option_elapsed_time(struct net_pkt *pkt, uint64_t since)
261 {
262 uint64_t elapsed;
263 int ret;
264
265 ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_ELAPSED_TIME,
266 DHCPV6_OPTION_ELAPSED_TIME_SIZE);
267 if (ret < 0) {
268 return ret;
269 }
270
271 /* Elapsed time should be expressed in hundredths of a second. */
272 elapsed = (k_uptime_get() - since) / 10ULL;
273 if (elapsed > 0xFFFF) {
274 elapsed = 0xFFFF;
275 }
276
277 ret = net_pkt_write_be16(pkt, (uint16_t)elapsed);
278
279 return ret;
280 }
281
dhcpv6_add_option_ia_na(struct net_pkt * pkt,struct dhcpv6_ia_na * ia_na,bool include_addr)282 static int dhcpv6_add_option_ia_na(struct net_pkt *pkt, struct dhcpv6_ia_na *ia_na,
283 bool include_addr)
284 {
285 uint16_t optlen;
286 int ret;
287
288 if (!include_addr) {
289 optlen = DHCPV6_OPTION_IA_NA_HEADER_SIZE;
290 } else {
291 optlen = DHCPV6_OPTION_IA_NA_HEADER_SIZE +
292 DHCPV6_OPTION_HEADER_SIZE +
293 DHCPV6_OPTION_IAADDR_HEADER_SIZE;
294 }
295
296 ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_IA_NA, optlen);
297 if (ret < 0) {
298 return ret;
299 }
300
301 ret = net_pkt_write_be32(pkt, ia_na->iaid);
302 if (ret < 0) {
303 return ret;
304 }
305
306 ret = net_pkt_write_be32(pkt, ia_na->t1);
307 if (ret < 0) {
308 return ret;
309 }
310
311 ret = net_pkt_write_be32(pkt, ia_na->t2);
312 if (ret < 0) {
313 return ret;
314 }
315
316 if (!include_addr) {
317 return 0;
318 }
319
320 ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_IAADDR,
321 DHCPV6_OPTION_IAADDR_HEADER_SIZE);
322 if (ret < 0) {
323 return ret;
324 }
325
326 ret = net_pkt_write(pkt, &ia_na->iaaddr.addr, sizeof(ia_na->iaaddr.addr));
327 if (ret < 0) {
328 return ret;
329 }
330
331 ret = net_pkt_write_be32(pkt, ia_na->iaaddr.preferred_lifetime);
332 if (ret < 0) {
333 return ret;
334 }
335
336 ret = net_pkt_write_be32(pkt, ia_na->iaaddr.valid_lifetime);
337
338 return ret;
339 }
340
dhcpv6_add_option_ia_pd(struct net_pkt * pkt,struct dhcpv6_ia_pd * ia_pd,bool include_prefix)341 static int dhcpv6_add_option_ia_pd(struct net_pkt *pkt, struct dhcpv6_ia_pd *ia_pd,
342 bool include_prefix)
343 {
344 uint16_t optlen;
345 int ret;
346
347 if (!include_prefix) {
348 optlen = DHCPV6_OPTION_IA_PD_HEADER_SIZE;
349 } else {
350 optlen = DHCPV6_OPTION_IA_PD_HEADER_SIZE +
351 DHCPV6_OPTION_HEADER_SIZE +
352 DHCPV6_OPTION_IAPREFIX_HEADER_SIZE;
353 }
354
355 ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_IA_PD,
356 optlen);
357 if (ret < 0) {
358 return ret;
359 }
360
361 ret = net_pkt_write_be32(pkt, ia_pd->iaid);
362 if (ret < 0) {
363 return ret;
364 }
365
366 ret = net_pkt_write_be32(pkt, ia_pd->t1);
367 if (ret < 0) {
368 return ret;
369 }
370
371 ret = net_pkt_write_be32(pkt, ia_pd->t2);
372 if (ret < 0) {
373 return ret;
374 }
375
376 if (!include_prefix) {
377 return 0;
378 }
379
380 ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_IAPREFIX,
381 DHCPV6_OPTION_IAPREFIX_HEADER_SIZE);
382 if (ret < 0) {
383 return ret;
384 }
385
386 ret = net_pkt_write_be32(pkt, ia_pd->iaprefix.preferred_lifetime);
387 if (ret < 0) {
388 return ret;
389 }
390
391 ret = net_pkt_write_be32(pkt, ia_pd->iaprefix.valid_lifetime);
392 if (ret < 0) {
393 return ret;
394 }
395
396 ret = net_pkt_write_u8(pkt, ia_pd->iaprefix.prefix_len);
397 if (ret < 0) {
398 return ret;
399 }
400
401 ret = net_pkt_write(pkt, &ia_pd->iaprefix.prefix,
402 sizeof(ia_pd->iaprefix.prefix));
403
404 return ret;
405 }
406
dhcpv6_add_option_oro(struct net_pkt * pkt,uint16_t * codes,int code_cnt)407 static int dhcpv6_add_option_oro(struct net_pkt *pkt, uint16_t *codes,
408 int code_cnt)
409 {
410 int ret;
411
412 ret = dhcpv6_add_option_header(pkt, DHCPV6_OPTION_CODE_ORO,
413 sizeof(uint16_t) * code_cnt);
414 if (ret < 0) {
415 return ret;
416 }
417
418 for (int i = 0; i < code_cnt; i++) {
419 ret = net_pkt_write_be16(pkt, codes[i]);
420 if (ret < 0) {
421 return ret;
422 }
423 }
424
425 return ret;
426 }
427
dhcpv6_calculate_message_size(struct dhcpv6_options_include * options)428 static size_t dhcpv6_calculate_message_size(struct dhcpv6_options_include *options)
429 {
430 size_t msg_size = sizeof(struct dhcpv6_msg_hdr);
431 uint8_t oro_cnt = 0;
432
433 if (options->clientid) {
434 msg_size += DHCPV6_OPTION_HEADER_SIZE;
435 msg_size += sizeof(struct net_dhcpv6_duid_storage);
436 }
437
438 if (options->serverid) {
439 msg_size += DHCPV6_OPTION_HEADER_SIZE;
440 msg_size += sizeof(struct net_dhcpv6_duid_storage);
441 }
442
443 if (options->elapsed_time) {
444 msg_size += DHCPV6_OPTION_HEADER_SIZE;
445 msg_size += DHCPV6_OPTION_ELAPSED_TIME_SIZE;
446 }
447
448 if (options->ia_na) {
449 msg_size += DHCPV6_OPTION_HEADER_SIZE;
450 msg_size += DHCPV6_OPTION_IA_NA_HEADER_SIZE;
451 }
452
453 if (options->iaaddr) {
454 msg_size += DHCPV6_OPTION_HEADER_SIZE;
455 msg_size += DHCPV6_OPTION_IAADDR_HEADER_SIZE;
456 }
457
458 if (options->ia_pd) {
459 msg_size += DHCPV6_OPTION_HEADER_SIZE;
460 msg_size += DHCPV6_OPTION_IA_PD_HEADER_SIZE;
461 }
462
463 if (options->iaprefix) {
464 msg_size += DHCPV6_OPTION_HEADER_SIZE;
465 msg_size += DHCPV6_OPTION_IAPREFIX_HEADER_SIZE;
466 }
467
468 for (uint8_t i = 0; i < ARRAY_SIZE(options->oro); i++) {
469 if (options->oro[i] == 0) {
470 break;
471 }
472
473 oro_cnt++;
474 }
475
476 if (oro_cnt > 0) {
477 msg_size += DHCPV6_OPTION_HEADER_SIZE;
478 msg_size += oro_cnt * sizeof(uint16_t);
479 }
480
481 return msg_size;
482 }
483
dhcpv6_add_options(struct net_if * iface,struct net_pkt * pkt,struct dhcpv6_options_include * options)484 static int dhcpv6_add_options(struct net_if *iface, struct net_pkt *pkt,
485 struct dhcpv6_options_include *options)
486 {
487 uint8_t oro_cnt = 0;
488 int ret;
489
490 if (options->clientid) {
491 ret = dhcpv6_add_option_clientid(
492 pkt, &iface->config.dhcpv6.clientid);
493 if (ret < 0) {
494 goto fail;
495 }
496 }
497
498 if (options->serverid) {
499 ret = dhcpv6_add_option_serverid(
500 pkt, &iface->config.dhcpv6.serverid);
501 if (ret < 0) {
502 goto fail;
503 }
504 }
505
506 if (options->elapsed_time) {
507 ret = dhcpv6_add_option_elapsed_time(
508 pkt, iface->config.dhcpv6.exchange_start);
509 if (ret < 0) {
510 goto fail;
511 }
512 }
513
514 if (options->ia_na) {
515 struct dhcpv6_ia_na ia_na = {
516 .iaid = iface->config.dhcpv6.addr_iaid,
517 };
518
519 if (options->iaaddr) {
520 memcpy(&ia_na.iaaddr.addr, &iface->config.dhcpv6.addr,
521 sizeof(ia_na.iaaddr.addr));
522 }
523
524 ret = dhcpv6_add_option_ia_na(pkt, &ia_na, options->iaaddr);
525 if (ret < 0) {
526 goto fail;
527 }
528 }
529
530 if (options->ia_pd) {
531 struct dhcpv6_ia_pd ia_pd = {
532 .iaid = iface->config.dhcpv6.prefix_iaid,
533 };
534
535 if (options->iaprefix) {
536 memcpy(&ia_pd.iaprefix.prefix, &iface->config.dhcpv6.prefix,
537 sizeof(ia_pd.iaprefix.prefix));
538 ia_pd.iaprefix.prefix_len = iface->config.dhcpv6.prefix_len;
539 }
540
541 ret = dhcpv6_add_option_ia_pd(pkt, &ia_pd, options->iaprefix);
542 if (ret < 0) {
543 goto fail;
544 }
545 }
546
547 for (uint8_t i = 0; i < ARRAY_SIZE(options->oro); i++) {
548 if (options->oro[i] == 0) {
549 break;
550 }
551
552 oro_cnt++;
553 }
554
555 if (oro_cnt > 0) {
556 ret = dhcpv6_add_option_oro(pkt, options->oro, oro_cnt);
557 if (ret < 0) {
558 goto fail;
559 }
560 }
561
562 return 0;
563
564 fail:
565 return ret;
566 }
567
dhcpv6_create_message(struct net_if * iface,enum dhcpv6_msg_type msg_type,struct dhcpv6_options_include * options)568 static struct net_pkt *dhcpv6_create_message(struct net_if *iface,
569 enum dhcpv6_msg_type msg_type,
570 struct dhcpv6_options_include *options)
571 {
572 struct in6_addr *local_addr;
573 struct net_pkt *pkt;
574 size_t msg_size;
575
576 local_addr = net_if_ipv6_get_ll(iface, NET_ADDR_ANY_STATE);
577 if (local_addr == NULL) {
578 NET_ERR("No LL address");
579 return NULL;
580 }
581
582 msg_size = dhcpv6_calculate_message_size(options);
583
584 pkt = net_pkt_alloc_with_buffer(iface, msg_size, AF_INET6,
585 IPPROTO_UDP, K_FOREVER);
586 if (pkt == NULL) {
587 return NULL;
588 }
589
590 if (net_ipv6_create(pkt, local_addr, &all_dhcpv6_ra_and_servers) < 0 ||
591 net_udp_create(pkt, htons(DHCPV6_CLIENT_PORT),
592 htons(DHCPV6_SERVER_PORT)) < 0) {
593 goto fail;
594 }
595
596 dhcpv6_generate_tid(iface);
597
598 if (dhcpv6_add_header(pkt, msg_type, iface->config.dhcpv6.tid) < 0) {
599 goto fail;
600 }
601
602 if (dhcpv6_add_options(iface, pkt, options) < 0) {
603 goto fail;
604 }
605
606 net_pkt_cursor_init(pkt);
607 net_ipv6_finalize(pkt, IPPROTO_UDP);
608
609 return pkt;
610
611 fail:
612 net_pkt_unref(pkt);
613
614 return NULL;
615 }
616
dhcpv6_send_solicit(struct net_if * iface)617 static int dhcpv6_send_solicit(struct net_if *iface)
618 {
619 int ret;
620 struct net_pkt *pkt;
621 struct dhcpv6_options_include options = {
622 .clientid = true,
623 .elapsed_time = true,
624 .ia_na = iface->config.dhcpv6.params.request_addr,
625 .ia_pd = iface->config.dhcpv6.params.request_prefix,
626 .oro = { DHCPV6_OPTION_CODE_SOL_MAX_RT },
627 };
628
629 pkt = dhcpv6_create_message(iface, DHCPV6_MSG_TYPE_SOLICIT, &options);
630 if (pkt == NULL) {
631 return -ENOMEM;
632 }
633
634 ret = net_send_data(pkt);
635 if (ret < 0) {
636 net_pkt_unref(pkt);
637 }
638
639 return ret;
640 }
641
dhcpv6_send_request(struct net_if * iface)642 static int dhcpv6_send_request(struct net_if *iface)
643 {
644 int ret;
645 struct net_pkt *pkt;
646 struct dhcpv6_options_include options = {
647 .clientid = true,
648 .serverid = true,
649 .elapsed_time = true,
650 .ia_na = iface->config.dhcpv6.params.request_addr,
651 .ia_pd = iface->config.dhcpv6.params.request_prefix,
652 .oro = { DHCPV6_OPTION_CODE_SOL_MAX_RT },
653 };
654
655 pkt = dhcpv6_create_message(iface, DHCPV6_MSG_TYPE_REQUEST, &options);
656 if (pkt == NULL) {
657 return -ENOMEM;
658 }
659
660 ret = net_send_data(pkt);
661 if (ret < 0) {
662 net_pkt_unref(pkt);
663 }
664
665 return ret;
666 }
667
dhcpv6_send_renew(struct net_if * iface)668 static int dhcpv6_send_renew(struct net_if *iface)
669 {
670 int ret;
671 struct net_pkt *pkt;
672 struct dhcpv6_options_include options = {
673 .clientid = true,
674 .serverid = true,
675 .elapsed_time = true,
676 .ia_na = iface->config.dhcpv6.params.request_addr,
677 .iaaddr = iface->config.dhcpv6.params.request_addr,
678 .ia_pd = iface->config.dhcpv6.params.request_prefix,
679 .iaprefix = iface->config.dhcpv6.params.request_prefix,
680 .oro = { DHCPV6_OPTION_CODE_SOL_MAX_RT },
681 };
682
683 pkt = dhcpv6_create_message(iface, DHCPV6_MSG_TYPE_RENEW, &options);
684 if (pkt == NULL) {
685 return -ENOMEM;
686 }
687
688 ret = net_send_data(pkt);
689 if (ret < 0) {
690 net_pkt_unref(pkt);
691 }
692
693 return ret;
694 }
695
dhcpv6_send_rebind(struct net_if * iface)696 static int dhcpv6_send_rebind(struct net_if *iface)
697 {
698 int ret;
699 struct net_pkt *pkt;
700 struct dhcpv6_options_include options = {
701 .clientid = true,
702 .elapsed_time = true,
703 .ia_na = iface->config.dhcpv6.params.request_addr,
704 .iaaddr = iface->config.dhcpv6.params.request_addr,
705 .ia_pd = iface->config.dhcpv6.params.request_prefix,
706 .iaprefix = iface->config.dhcpv6.params.request_prefix,
707 .oro = { DHCPV6_OPTION_CODE_SOL_MAX_RT },
708 };
709
710 pkt = dhcpv6_create_message(iface, DHCPV6_MSG_TYPE_REBIND, &options);
711 if (pkt == NULL) {
712 return -ENOMEM;
713 }
714
715 ret = net_send_data(pkt);
716 if (ret < 0) {
717 net_pkt_unref(pkt);
718 }
719
720 return ret;
721 }
722
dhcpv6_send_confirm(struct net_if * iface)723 static int dhcpv6_send_confirm(struct net_if *iface)
724 {
725 int ret;
726 struct net_pkt *pkt;
727 struct dhcpv6_options_include options = {
728 .clientid = true,
729 .elapsed_time = true,
730 .ia_na = true,
731 .iaaddr = true,
732 };
733
734 pkt = dhcpv6_create_message(iface, DHCPV6_MSG_TYPE_CONFIRM, &options);
735 if (pkt == NULL) {
736 return -ENOMEM;
737 }
738
739 ret = net_send_data(pkt);
740 if (ret < 0) {
741 net_pkt_unref(pkt);
742 }
743
744 return ret;
745 }
746
747 /* DHCPv6 packet parsing functions */
748
dhcpv6_parse_option_clientid(struct net_pkt * pkt,uint16_t length,struct net_dhcpv6_duid_storage * clientid)749 static int dhcpv6_parse_option_clientid(struct net_pkt *pkt, uint16_t length,
750 struct net_dhcpv6_duid_storage *clientid)
751 {
752 struct net_dhcpv6_duid_raw duid;
753 int ret;
754
755 if (length > sizeof(struct net_dhcpv6_duid_raw)) {
756 NET_ERR("DUID too large to handle");
757 return -EMSGSIZE;
758 }
759
760 ret = net_pkt_read(pkt, &duid, length);
761 if (ret < 0) {
762 return ret;
763 }
764
765 clientid->length = length;
766 memcpy(&clientid->duid, &duid, length);
767
768 return 0;
769 }
770
dhcpv6_parse_option_serverid(struct net_pkt * pkt,uint16_t length,struct net_dhcpv6_duid_storage * serverid)771 static int dhcpv6_parse_option_serverid(struct net_pkt *pkt, uint16_t length,
772 struct net_dhcpv6_duid_storage *serverid)
773 {
774 struct net_dhcpv6_duid_raw duid;
775 int ret;
776
777 if (length > sizeof(struct net_dhcpv6_duid_raw)) {
778 NET_ERR("DUID too large to handle");
779 return -EMSGSIZE;
780 }
781
782 ret = net_pkt_read(pkt, &duid, length);
783 if (ret < 0) {
784 return ret;
785 }
786
787 serverid->length = length;
788 memcpy(&serverid->duid, &duid, length);
789
790 return 0;
791 }
792
dhcpv6_parse_option_preference(struct net_pkt * pkt,uint16_t length,uint8_t * preference)793 static int dhcpv6_parse_option_preference(struct net_pkt *pkt, uint16_t length,
794 uint8_t *preference)
795 {
796 if (length != DHCPV6_OPTION_PREFERENCE_SIZE) {
797 return -EBADMSG;
798 }
799
800 if (net_pkt_read_u8(pkt, preference) < 0) {
801 return -EBADMSG;
802 }
803
804 return 0;
805 }
806
dhcpv6_parse_option_status_code(struct net_pkt * pkt,uint16_t length,uint16_t * status)807 static int dhcpv6_parse_option_status_code(struct net_pkt *pkt,
808 uint16_t length, uint16_t *status)
809 {
810 int ret;
811
812 if (length < DHCPV6_OPTION_STATUS_CODE_HEADER_SIZE) {
813 NET_ERR("Invalid IAADDR option size");
814 return -EMSGSIZE;
815 }
816
817 ret = net_pkt_read_be16(pkt, status);
818 if (ret < 0) {
819 return ret;
820 }
821
822 NET_DBG("status code %d", *status);
823
824 length -= DHCPV6_OPTION_STATUS_CODE_HEADER_SIZE;
825 if (length > 0) {
826 /* Ignore status message */
827 ret = net_pkt_skip(pkt, length);
828 }
829
830 return ret;
831 }
832
dhcpv6_parse_option_iaaddr(struct net_pkt * pkt,uint16_t length,struct dhcpv6_iaaddr * iaaddr)833 static int dhcpv6_parse_option_iaaddr(struct net_pkt *pkt, uint16_t length,
834 struct dhcpv6_iaaddr *iaaddr)
835 {
836 int ret;
837
838 if (length < DHCPV6_OPTION_IAADDR_HEADER_SIZE) {
839 NET_ERR("Invalid IAADDR option size");
840 return -EMSGSIZE;
841 }
842
843 ret = net_pkt_read(pkt, &iaaddr->addr, sizeof(iaaddr->addr));
844 if (ret < 0) {
845 return ret;
846 }
847
848 ret = net_pkt_read_be32(pkt, &iaaddr->preferred_lifetime);
849 if (ret < 0) {
850 return ret;
851 }
852
853 ret = net_pkt_read_be32(pkt, &iaaddr->valid_lifetime);
854 if (ret < 0) {
855 return ret;
856 }
857
858 /* DHCPv6 RFC8415, ch. 21.6 The client MUST discard any addresses for
859 * which the preferred lifetime is greater than the valid lifetime.
860 */
861 if (iaaddr->preferred_lifetime > iaaddr->valid_lifetime) {
862 return -EBADMSG;
863 }
864
865 NET_DBG("addr %s preferred_lifetime %d valid_lifetime %d",
866 net_sprint_ipv6_addr(&iaaddr->addr), iaaddr->preferred_lifetime,
867 iaaddr->valid_lifetime);
868
869 iaaddr->status = DHCPV6_STATUS_SUCCESS;
870
871 length -= DHCPV6_OPTION_IAADDR_HEADER_SIZE;
872 while (length > 0) {
873 uint16_t code, sublen;
874
875 ret = net_pkt_read_be16(pkt, &code);
876 if (ret < 0) {
877 return ret;
878 }
879
880 ret = net_pkt_read_be16(pkt, &sublen);
881 if (ret < 0) {
882 return ret;
883 }
884
885 switch (code) {
886 case DHCPV6_OPTION_CODE_STATUS_CODE:
887 ret = dhcpv6_parse_option_status_code(pkt, sublen,
888 &iaaddr->status);
889 if (ret < 0) {
890 return ret;
891 }
892
893 break;
894 default:
895 NET_DBG("Unexpected option %d length %d", code, sublen);
896
897 ret = net_pkt_skip(pkt, sublen);
898 if (ret < 0) {
899 return ret;
900 }
901
902 break;
903 }
904
905 length -= (sublen + 4);
906 }
907
908 return 0;
909 }
910
dhcpv6_parse_option_ia_na(struct net_pkt * pkt,uint16_t length,struct dhcpv6_ia_na * ia_na)911 static int dhcpv6_parse_option_ia_na(struct net_pkt *pkt, uint16_t length,
912 struct dhcpv6_ia_na *ia_na)
913 {
914 int ret;
915
916 if (length < DHCPV6_OPTION_IA_NA_HEADER_SIZE) {
917 NET_ERR("Invalid IA_NA option size");
918 return -EMSGSIZE;
919 }
920
921 ret = net_pkt_read_be32(pkt, &ia_na->iaid);
922 if (ret < 0) {
923 return ret;
924 }
925
926 ret = net_pkt_read_be32(pkt, &ia_na->t1);
927 if (ret < 0) {
928 return ret;
929 }
930
931 ret = net_pkt_read_be32(pkt, &ia_na->t2);
932 if (ret < 0) {
933 return ret;
934 }
935
936 /* DHCPv6 RFC8415, ch. 21.4 If a client receives an IA_NA with T1
937 * greater than T2 and both T1 and T2 are greater than 0, the client
938 * discards the IA_NA option and processes the remainder of the message
939 * as though the server had not included the invalid IA_NA option.
940 */
941 if (ia_na->t1 != 0 && ia_na->t2 != 0 && ia_na->t1 > ia_na->t2) {
942 return -ENOENT;
943 }
944
945 NET_DBG("iaid %d t1 %d t2 %d", ia_na->iaid, ia_na->t1, ia_na->t2);
946
947 /* In case there's no IAADDR option, make this visible be setting
948 * error status. If the option is present, option parser will overwrite
949 * the value.
950 */
951 ia_na->iaaddr.status = DHCPV6_STATUS_NO_ADDR_AVAIL;
952 ia_na->status = DHCPV6_STATUS_SUCCESS;
953
954 length -= DHCPV6_OPTION_IA_NA_HEADER_SIZE;
955 while (length > 0) {
956 uint16_t code, sublen;
957
958 ret = net_pkt_read_be16(pkt, &code);
959 if (ret < 0) {
960 return ret;
961 }
962
963 ret = net_pkt_read_be16(pkt, &sublen);
964 if (ret < 0) {
965 return ret;
966 }
967
968 switch (code) {
969 case DHCPV6_OPTION_CODE_IAADDR:
970 ret = dhcpv6_parse_option_iaaddr(pkt, sublen,
971 &ia_na->iaaddr);
972 if (ret < 0) {
973 return ret;
974 }
975
976 break;
977
978 case DHCPV6_OPTION_CODE_STATUS_CODE:
979 ret = dhcpv6_parse_option_status_code(pkt, sublen,
980 &ia_na->status);
981 if (ret < 0) {
982 return ret;
983 }
984
985 break;
986
987 default:
988 NET_DBG("Unexpected option %d length %d", code, sublen);
989
990 ret = net_pkt_skip(pkt, sublen);
991 if (ret < 0) {
992 return ret;
993 }
994
995 break;
996 }
997
998 length -= (sublen + 4);
999 }
1000
1001 return 0;
1002 }
1003
dhcpv6_parse_option_iaprefix(struct net_pkt * pkt,uint16_t length,struct dhcpv6_iaprefix * iaprefix)1004 static int dhcpv6_parse_option_iaprefix(struct net_pkt *pkt, uint16_t length,
1005 struct dhcpv6_iaprefix *iaprefix)
1006 {
1007 int ret;
1008
1009 if (length < DHCPV6_OPTION_IAPREFIX_HEADER_SIZE) {
1010 NET_ERR("Invalid IAPREFIX option size");
1011 return -EMSGSIZE;
1012 }
1013
1014 ret = net_pkt_read_be32(pkt, &iaprefix->preferred_lifetime);
1015 if (ret < 0) {
1016 return ret;
1017 }
1018
1019 ret = net_pkt_read_be32(pkt, &iaprefix->valid_lifetime);
1020 if (ret < 0) {
1021 return ret;
1022 }
1023
1024 ret = net_pkt_read_u8(pkt, &iaprefix->prefix_len);
1025 if (ret < 0) {
1026 return ret;
1027 }
1028
1029 ret = net_pkt_read(pkt, &iaprefix->prefix, sizeof(iaprefix->prefix));
1030 if (ret < 0) {
1031 return ret;
1032 }
1033
1034 /* DHCPv6 RFC8415, ch. 21.22 The client MUST discard any prefixes for
1035 * which the preferred lifetime is greater than the valid lifetime.
1036 */
1037 if (iaprefix->preferred_lifetime > iaprefix->valid_lifetime) {
1038 return -EBADMSG;
1039 }
1040
1041 NET_DBG("prefix %s/%u preferred_lifetime %d valid_lifetime %d",
1042 net_sprint_ipv6_addr(&iaprefix->prefix), iaprefix->prefix_len,
1043 iaprefix->preferred_lifetime, iaprefix->valid_lifetime);
1044
1045 iaprefix->status = DHCPV6_STATUS_SUCCESS;
1046
1047 length -= DHCPV6_OPTION_IAPREFIX_HEADER_SIZE;
1048 while (length > 0) {
1049 uint16_t code, sublen;
1050
1051 ret = net_pkt_read_be16(pkt, &code);
1052 if (ret < 0) {
1053 return ret;
1054 }
1055
1056 ret = net_pkt_read_be16(pkt, &sublen);
1057 if (ret < 0) {
1058 return ret;
1059 }
1060
1061 switch (code) {
1062 case DHCPV6_OPTION_CODE_STATUS_CODE:
1063 ret = dhcpv6_parse_option_status_code(pkt, sublen,
1064 &iaprefix->status);
1065 if (ret < 0) {
1066 return ret;
1067 }
1068
1069 break;
1070 default:
1071 NET_DBG("Unexpected option %d length %d", code, sublen);
1072
1073 ret = net_pkt_skip(pkt, sublen);
1074 if (ret < 0) {
1075 return ret;
1076 }
1077
1078 break;
1079 }
1080
1081 length -= (sublen + 4);
1082 }
1083
1084 return 0;
1085 }
1086
dhcpv6_parse_option_ia_pd(struct net_pkt * pkt,uint16_t length,struct dhcpv6_ia_pd * ia_pd)1087 static int dhcpv6_parse_option_ia_pd(struct net_pkt *pkt, uint16_t length,
1088 struct dhcpv6_ia_pd *ia_pd)
1089 {
1090 int ret;
1091
1092 if (length < DHCPV6_OPTION_IA_PD_HEADER_SIZE) {
1093 NET_ERR("Invalid IA_PD option size");
1094 return -EMSGSIZE;
1095 }
1096
1097 ret = net_pkt_read_be32(pkt, &ia_pd->iaid);
1098 if (ret < 0) {
1099 return ret;
1100 }
1101
1102 ret = net_pkt_read_be32(pkt, &ia_pd->t1);
1103 if (ret < 0) {
1104 return ret;
1105 }
1106
1107 ret = net_pkt_read_be32(pkt, &ia_pd->t2);
1108 if (ret < 0) {
1109 return ret;
1110 }
1111
1112 /* DHCPv6 RFC8415, ch. 21.21 If a client receives an IA_PD with T1
1113 * greater than T2 and both T1 and T2 are greater than 0, the client
1114 * discards the IA_PD option and processes the remainder of the message
1115 * as though the server had not included the IA_PD option.
1116 */
1117 if (ia_pd->t1 != 0 && ia_pd->t2 != 0 && ia_pd->t1 > ia_pd->t2) {
1118 return -ENOENT;
1119 }
1120
1121 NET_DBG("iaid %d t1 %d t2 %d", ia_pd->iaid, ia_pd->t1, ia_pd->t2);
1122
1123 /* In case there's no IAPREFIX option, make this visible be setting
1124 * error status. If the option is present, option parser will overwrite
1125 * the value.
1126 */
1127 ia_pd->iaprefix.status = DHCPV6_STATUS_NO_PREFIX_AVAIL;
1128 ia_pd->status = DHCPV6_STATUS_SUCCESS;
1129
1130 length -= DHCPV6_OPTION_IA_PD_HEADER_SIZE;
1131 while (length > 0) {
1132 uint16_t code, sublen;
1133
1134 ret = net_pkt_read_be16(pkt, &code);
1135 if (ret < 0) {
1136 return ret;
1137 }
1138
1139 ret = net_pkt_read_be16(pkt, &sublen);
1140 if (ret < 0) {
1141 return ret;
1142 }
1143
1144 switch (code) {
1145 case DHCPV6_OPTION_CODE_IAPREFIX:
1146 ret = dhcpv6_parse_option_iaprefix(pkt, sublen,
1147 &ia_pd->iaprefix);
1148 if (ret < 0) {
1149 return ret;
1150 }
1151
1152 break;
1153
1154 case DHCPV6_OPTION_CODE_STATUS_CODE:
1155 ret = dhcpv6_parse_option_status_code(pkt, sublen,
1156 &ia_pd->status);
1157 if (ret < 0) {
1158 return ret;
1159 }
1160
1161 break;
1162 default:
1163 NET_DBG("Unexpected option %d length %d", code, sublen);
1164
1165 ret = net_pkt_skip(pkt, sublen);
1166 if (ret < 0) {
1167 return ret;
1168 }
1169
1170 break;
1171 }
1172
1173 length -= (sublen + 4);
1174 }
1175
1176 return 0;
1177 }
1178
dhcpv6_find_option(struct net_pkt * pkt,enum dhcpv6_option_code opt_code,uint16_t * opt_len)1179 static int dhcpv6_find_option(struct net_pkt *pkt, enum dhcpv6_option_code opt_code,
1180 uint16_t *opt_len)
1181 {
1182 uint16_t length;
1183 uint16_t code;
1184 int ret;
1185
1186 while (net_pkt_read_be16(pkt, &code) == 0) {
1187 if (net_pkt_read_be16(pkt, &length) < 0) {
1188 return -EBADMSG;
1189 }
1190
1191 if (code == opt_code) {
1192 *opt_len = length;
1193 return 0;
1194 }
1195
1196 ret = net_pkt_skip(pkt, length);
1197 if (ret < 0) {
1198 return ret;
1199 }
1200 }
1201
1202 return -ENOENT;
1203 }
1204
dhcpv6_find_clientid(struct net_pkt * pkt,struct net_dhcpv6_duid_storage * clientid)1205 static int dhcpv6_find_clientid(struct net_pkt *pkt,
1206 struct net_dhcpv6_duid_storage *clientid)
1207 {
1208 struct net_pkt_cursor backup;
1209 uint16_t length;
1210 int ret;
1211
1212 net_pkt_cursor_backup(pkt, &backup);
1213
1214 ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_CLIENTID, &length);
1215 if (ret == 0) {
1216 ret = dhcpv6_parse_option_clientid(pkt, length, clientid);
1217 }
1218
1219 net_pkt_cursor_restore(pkt, &backup);
1220
1221 return ret;
1222 }
1223
dhcpv6_find_serverid(struct net_pkt * pkt,struct net_dhcpv6_duid_storage * serverid)1224 static int dhcpv6_find_serverid(struct net_pkt *pkt,
1225 struct net_dhcpv6_duid_storage *serverid)
1226 {
1227 struct net_pkt_cursor backup;
1228 uint16_t length;
1229 int ret;
1230
1231 net_pkt_cursor_backup(pkt, &backup);
1232
1233 ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_SERVERID, &length);
1234 if (ret == 0) {
1235 ret = dhcpv6_parse_option_serverid(pkt, length, serverid);
1236 }
1237
1238 net_pkt_cursor_restore(pkt, &backup);
1239
1240 return ret;
1241 }
1242
dhcpv6_find_server_preference(struct net_pkt * pkt,uint8_t * preference)1243 static int dhcpv6_find_server_preference(struct net_pkt *pkt,
1244 uint8_t *preference)
1245 {
1246 struct net_pkt_cursor backup;
1247 uint16_t length;
1248 int ret;
1249
1250 net_pkt_cursor_backup(pkt, &backup);
1251
1252 ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_PREFERENCE, &length);
1253 if (ret == 0) {
1254 ret = dhcpv6_parse_option_preference(pkt, length, preference);
1255 } else if (ret == -ENOENT) {
1256 /* In case no preference option is present, default to 0.
1257 * DHCPv6 RFC8415, ch. 18.2.1.
1258 */
1259 *preference = 0;
1260 ret = 0;
1261 }
1262
1263 net_pkt_cursor_restore(pkt, &backup);
1264
1265 return ret;
1266 }
1267
dhcpv6_find_ia_na(struct net_pkt * pkt,struct dhcpv6_ia_na * ia_na)1268 static int dhcpv6_find_ia_na(struct net_pkt *pkt, struct dhcpv6_ia_na *ia_na)
1269 {
1270 struct net_pkt_cursor backup;
1271 uint16_t length;
1272 int ret;
1273
1274 net_pkt_cursor_backup(pkt, &backup);
1275
1276 ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_IA_NA, &length);
1277 if (ret == 0) {
1278 ret = dhcpv6_parse_option_ia_na(pkt, length, ia_na);
1279 }
1280
1281 net_pkt_cursor_restore(pkt, &backup);
1282
1283 return ret;
1284 }
1285
dhcpv6_find_ia_pd(struct net_pkt * pkt,struct dhcpv6_ia_pd * ia_pd)1286 static int dhcpv6_find_ia_pd(struct net_pkt *pkt, struct dhcpv6_ia_pd *ia_pd)
1287 {
1288 struct net_pkt_cursor backup;
1289 uint16_t length;
1290 int ret;
1291
1292 net_pkt_cursor_backup(pkt, &backup);
1293
1294 ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_IA_PD, &length);
1295 if (ret == 0) {
1296 ret = dhcpv6_parse_option_ia_pd(pkt, length, ia_pd);
1297 }
1298
1299 net_pkt_cursor_restore(pkt, &backup);
1300
1301 return ret;
1302 }
1303
dhcpv6_find_status_code(struct net_pkt * pkt,uint16_t * status)1304 static int dhcpv6_find_status_code(struct net_pkt *pkt, uint16_t *status)
1305 {
1306 struct net_pkt_cursor backup;
1307 uint16_t length;
1308 int ret;
1309
1310 net_pkt_cursor_backup(pkt, &backup);
1311
1312 ret = dhcpv6_find_option(pkt, DHCPV6_OPTION_CODE_STATUS_CODE, &length);
1313 if (ret == 0) {
1314 ret = dhcpv6_parse_option_status_code(pkt, length, status);
1315 } else if (ret == -ENOENT) {
1316 /* In case no status option is present, default to success.
1317 * DHCPv6 RFC8415, ch. 21.13.
1318 */
1319 *status = DHCPV6_STATUS_SUCCESS;
1320 ret = 0;
1321 }
1322
1323 net_pkt_cursor_restore(pkt, &backup);
1324
1325 return ret;
1326 }
1327
1328 /* DHCPv6 state changes */
1329
dhcpv6_enter_init(struct net_if * iface)1330 static void dhcpv6_enter_init(struct net_if *iface)
1331 {
1332 uint32_t timeout;
1333
1334 /* RFC8415 requires to wait a random period up to 1 second before
1335 * sending the initial solicit/information request/confirm.
1336 */
1337 timeout = sys_rand32_get() % DHCPV6_SOL_MAX_DELAY;
1338
1339 dhcpv6_set_timeout(iface, timeout);
1340 }
1341
dhcpv6_enter_soliciting(struct net_if * iface)1342 static void dhcpv6_enter_soliciting(struct net_if *iface)
1343 {
1344 iface->config.dhcpv6.retransmit_timeout =
1345 dhcpv6_initial_retransmit_time(DHCPV6_SOL_TIMEOUT);
1346 iface->config.dhcpv6.retransmissions = 0;
1347 iface->config.dhcpv6.server_preference = -1;
1348 iface->config.dhcpv6.exchange_start = k_uptime_get();
1349
1350 (void)dhcpv6_send_solicit(iface);
1351 dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout);
1352 }
1353
dhcpv6_enter_requesting(struct net_if * iface)1354 static void dhcpv6_enter_requesting(struct net_if *iface)
1355 {
1356 iface->config.dhcpv6.retransmit_timeout =
1357 dhcpv6_initial_retransmit_time(DHCPV6_REQ_TIMEOUT);
1358 iface->config.dhcpv6.retransmissions = 0;
1359 iface->config.dhcpv6.exchange_start = k_uptime_get();
1360
1361 (void)dhcpv6_send_request(iface);
1362 dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout);
1363 }
1364
dhcpv6_enter_renewing(struct net_if * iface)1365 static void dhcpv6_enter_renewing(struct net_if *iface)
1366 {
1367 iface->config.dhcpv6.retransmit_timeout =
1368 dhcpv6_initial_retransmit_time(DHCPV6_REN_TIMEOUT);
1369 iface->config.dhcpv6.retransmissions = 0;
1370 iface->config.dhcpv6.exchange_start = k_uptime_get();
1371
1372 (void)dhcpv6_send_renew(iface);
1373 dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout);
1374 }
1375
dhcpv6_enter_rebinding(struct net_if * iface)1376 static void dhcpv6_enter_rebinding(struct net_if *iface)
1377 {
1378 iface->config.dhcpv6.retransmit_timeout =
1379 dhcpv6_initial_retransmit_time(DHCPV6_REB_TIMEOUT);
1380 iface->config.dhcpv6.retransmissions = 0;
1381 iface->config.dhcpv6.exchange_start = k_uptime_get();
1382
1383 (void)dhcpv6_send_rebind(iface);
1384 dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout);
1385 }
1386
dhcpv6_enter_confirming(struct net_if * iface)1387 static void dhcpv6_enter_confirming(struct net_if *iface)
1388 {
1389 iface->config.dhcpv6.retransmit_timeout =
1390 dhcpv6_initial_retransmit_time(DHCPV6_CNF_TIMEOUT);
1391 iface->config.dhcpv6.retransmissions = 0;
1392 iface->config.dhcpv6.exchange_start = k_uptime_get();
1393
1394 (void)dhcpv6_send_confirm(iface);
1395 dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout);
1396 }
1397
dhcpv6_enter_bound(struct net_if * iface)1398 static void dhcpv6_enter_bound(struct net_if *iface)
1399 {
1400 iface->config.dhcpv6.timeout = iface->config.dhcpv6.t1;
1401
1402 net_mgmt_event_notify_with_info(NET_EVENT_IPV6_DHCP_BOUND, iface,
1403 &iface->config.dhcpv6,
1404 sizeof(iface->config.dhcpv6));
1405 }
1406
dhcpv6_enter_state(struct net_if * iface,enum net_dhcpv6_state state)1407 static void dhcpv6_enter_state(struct net_if *iface, enum net_dhcpv6_state state)
1408 {
1409 iface->config.dhcpv6.state = state;
1410
1411 NET_DBG("enter state=%s",
1412 net_dhcpv6_state_name(iface->config.dhcpv6.state));
1413
1414 switch (iface->config.dhcpv6.state) {
1415 case NET_DHCPV6_DISABLED:
1416 break;
1417 case NET_DHCPV6_INIT:
1418 return dhcpv6_enter_init(iface);
1419 case NET_DHCPV6_SOLICITING:
1420 return dhcpv6_enter_soliciting(iface);
1421 case NET_DHCPV6_REQUESTING:
1422 return dhcpv6_enter_requesting(iface);
1423 case NET_DHCPV6_CONFIRMING:
1424 return dhcpv6_enter_confirming(iface);
1425 case NET_DHCPV6_RENEWING:
1426 return dhcpv6_enter_renewing(iface);
1427 case NET_DHCPV6_REBINDING:
1428 return dhcpv6_enter_rebinding(iface);
1429 case NET_DHCPV6_INFO_REQUESTING:
1430 break;
1431 case NET_DHCPV6_BOUND:
1432 return dhcpv6_enter_bound(iface);
1433 }
1434 }
1435
1436 /* DHCPv6 input processing */
1437
dhcpv6_handle_advertise(struct net_if * iface,struct net_pkt * pkt,uint8_t * tid)1438 static int dhcpv6_handle_advertise(struct net_if *iface, struct net_pkt *pkt,
1439 uint8_t *tid)
1440 {
1441 struct net_dhcpv6_duid_storage duid = { 0 };
1442 struct dhcpv6_ia_pd ia_pd = { 0 };
1443 struct dhcpv6_ia_na ia_na = { 0 };
1444 uint8_t server_preference = 0;
1445 uint16_t status = 0;
1446 int ret;
1447
1448 if (iface->config.dhcpv6.state != NET_DHCPV6_SOLICITING) {
1449 return -EINVAL;
1450 }
1451
1452 /* Verify client ID. */
1453 ret = dhcpv6_find_clientid(pkt, &duid);
1454 if (ret < 0) {
1455 NET_ERR("Client ID missing");
1456 return ret;
1457 }
1458
1459 if (iface->config.dhcpv6.clientid.length != duid.length ||
1460 memcmp(&iface->config.dhcpv6.clientid.duid, &duid.duid,
1461 iface->config.dhcpv6.clientid.length) != 0) {
1462 NET_ERR("Client ID mismatch");
1463 return -EBADMSG;
1464 }
1465
1466 /* Verify server ID is present. */
1467 memset(&duid, 0, sizeof(duid));
1468 ret = dhcpv6_find_serverid(pkt, &duid);
1469 if (ret < 0) {
1470 NET_ERR("Server ID missing");
1471 return ret;
1472 }
1473
1474 /* Verify TID. */
1475 if (memcmp(iface->config.dhcpv6.tid, tid,
1476 sizeof(iface->config.dhcpv6.tid)) != 0) {
1477 NET_INFO("TID mismatch");
1478 return -EBADMSG;
1479 }
1480
1481 /* Verify status code. */
1482 ret = dhcpv6_find_status_code(pkt, &status);
1483 if (ret < 0) {
1484 return ret;
1485 }
1486
1487 if (status != DHCPV6_STATUS_SUCCESS) {
1488 /* Ignore. */
1489 return 0;
1490 }
1491
1492 /* TODO Process SOL_MAX_RT/INF_MAX_RT options. */
1493
1494 /* Verify server preference. */
1495 ret = dhcpv6_find_server_preference(pkt, &server_preference);
1496 if (ret < 0) {
1497 return ret;
1498 }
1499
1500 if ((int16_t)server_preference < iface->config.dhcpv6.server_preference) {
1501 /* Ignore. */
1502 return 0;
1503 }
1504
1505 /* Find/verify address. */
1506 if (iface->config.dhcpv6.params.request_addr) {
1507 ret = dhcpv6_find_ia_na(pkt, &ia_na);
1508 if (ret < 0) {
1509 NET_ERR("Address missing");
1510 return ret;
1511 }
1512
1513 if (ia_na.status != DHCPV6_STATUS_SUCCESS ||
1514 ia_na.iaaddr.status != DHCPV6_STATUS_SUCCESS) {
1515 /* Ignore. */
1516 return 0;
1517 }
1518 }
1519
1520 /* Find/verify prefix. */
1521 if (iface->config.dhcpv6.params.request_prefix) {
1522 ret = dhcpv6_find_ia_pd(pkt, &ia_pd);
1523 if (ret < 0) {
1524 NET_ERR("Prefix missing");
1525 return ret;
1526 }
1527
1528 if (ia_pd.status != DHCPV6_STATUS_SUCCESS ||
1529 ia_pd.iaprefix.status != DHCPV6_STATUS_SUCCESS) {
1530 /* Ignore. */
1531 return 0;
1532 }
1533 }
1534
1535 /* Valid advertisement received, store received offer. */
1536 memcpy(&iface->config.dhcpv6.serverid, &duid,
1537 sizeof(iface->config.dhcpv6.serverid));
1538 iface->config.dhcpv6.server_preference = server_preference;
1539
1540 /* DHCPv6 RFC8415, ch. 18.2.1, if client received Advertise
1541 * message with maximum preference, or after the first
1542 * retransmission period, it should proceed with the exchange,
1543 * w/o further wait.
1544 */
1545 if (server_preference == DHCPV6_MAX_SERVER_PREFERENCE ||
1546 iface->config.dhcpv6.retransmissions > 0) {
1547 /* Reschedule immediately */
1548 dhcpv6_enter_state(iface, NET_DHCPV6_REQUESTING);
1549 dhcpv6_reschedule();
1550 }
1551
1552 return 0;
1553 }
1554
dhcpv6_handle_reply(struct net_if * iface,struct net_pkt * pkt,uint8_t * tid)1555 static int dhcpv6_handle_reply(struct net_if *iface, struct net_pkt *pkt,
1556 uint8_t *tid)
1557 {
1558 struct net_dhcpv6_duid_storage duid = { 0 };
1559 struct dhcpv6_ia_pd ia_pd = { 0 };
1560 struct dhcpv6_ia_na ia_na = { 0 };
1561 int64_t now = k_uptime_get();
1562 uint16_t status = 0;
1563 bool rediscover = false;
1564 int ret;
1565
1566 if (iface->config.dhcpv6.state != NET_DHCPV6_REQUESTING &&
1567 iface->config.dhcpv6.state != NET_DHCPV6_CONFIRMING &&
1568 iface->config.dhcpv6.state != NET_DHCPV6_RENEWING &&
1569 iface->config.dhcpv6.state != NET_DHCPV6_REBINDING) {
1570 return -EINVAL;
1571 }
1572
1573 /* Verify client ID. */
1574 ret = dhcpv6_find_clientid(pkt, &duid);
1575 if (ret < 0) {
1576 NET_ERR("Client ID missing");
1577 return ret;
1578 }
1579
1580 if (iface->config.dhcpv6.clientid.length != duid.length ||
1581 memcmp(&iface->config.dhcpv6.clientid.duid, &duid.duid,
1582 iface->config.dhcpv6.clientid.length) != 0) {
1583 NET_ERR("Client ID mismatch");
1584 return -EBADMSG;
1585 }
1586
1587 /* Verify server ID is present. */
1588 memset(&duid, 0, sizeof(duid));
1589 ret = dhcpv6_find_serverid(pkt, &duid);
1590 if (ret < 0) {
1591 NET_ERR("Server ID missing");
1592 return ret;
1593 }
1594
1595 /* Verify TID. */
1596 if (memcmp(iface->config.dhcpv6.tid, tid,
1597 sizeof(iface->config.dhcpv6.tid)) != 0) {
1598 NET_INFO("TID mismatch");
1599 return -EBADMSG;
1600 }
1601
1602 /* TODO Process SOL_MAX_RT/INF_MAX_RT options. */
1603
1604 /* Verify status code. */
1605 ret = dhcpv6_find_status_code(pkt, &status);
1606 if (ret < 0) {
1607 return ret;
1608 }
1609
1610 if (status == DHCPV6_STATUS_UNSPEC_FAIL) {
1611 /* Ignore and try again later. */
1612 return 0;
1613 }
1614
1615 /* DHCPv6 RFC8415, ch. 18.2.10.1. If the client receives a NotOnLink
1616 * status from the server in response to (...) Request, the client can
1617 * either reissue the message without specifying any addresses or
1618 * restart the DHCP server discovery process.
1619 *
1620 * Restart discovery for our case.
1621 */
1622 if (iface->config.dhcpv6.state == NET_DHCPV6_REQUESTING &&
1623 status == DHCPV6_STATUS_NOT_ON_LINK) {
1624 rediscover = true;
1625 goto out;
1626 }
1627
1628 /* In case of Confirm Reply, status success indicates the client can
1629 * still use the address.
1630 */
1631 if (iface->config.dhcpv6.state == NET_DHCPV6_CONFIRMING) {
1632 if (status != DHCPV6_STATUS_SUCCESS) {
1633 rediscover = true;
1634 }
1635
1636 goto out;
1637 }
1638
1639 /* Find/verify address. */
1640 if (iface->config.dhcpv6.params.request_addr) {
1641 ret = dhcpv6_find_ia_na(pkt, &ia_na);
1642 if (ret < 0) {
1643 NET_ERR("Address missing");
1644 return ret;
1645 }
1646
1647 if (iface->config.dhcpv6.addr_iaid != ia_na.iaid) {
1648 return -EBADMSG;
1649 }
1650 }
1651
1652 /* Find/verify prefix. */
1653 if (iface->config.dhcpv6.params.request_prefix) {
1654 ret = dhcpv6_find_ia_pd(pkt, &ia_pd);
1655 if (ret < 0) {
1656 NET_ERR("Prefix missing");
1657 return ret;
1658 }
1659
1660 if (iface->config.dhcpv6.prefix_iaid != ia_pd.iaid) {
1661 return -EBADMSG;
1662 }
1663 }
1664
1665 /* Valid response received, store received data. */
1666 iface->config.dhcpv6.t1 = UINT64_MAX;
1667 iface->config.dhcpv6.t2 = UINT64_MAX;
1668 iface->config.dhcpv6.expire = now;
1669
1670 if (iface->config.dhcpv6.params.request_addr) {
1671 struct net_if_addr *ifaddr;
1672
1673 if (ia_na.status == DHCPV6_STATUS_NO_ADDR_AVAIL ||
1674 ia_na.iaaddr.status == DHCPV6_STATUS_NO_ADDR_AVAIL ||
1675 ia_na.iaaddr.valid_lifetime == 0) {
1676 /* Remove old lease. */
1677 net_if_ipv6_addr_rm(iface, &iface->config.dhcpv6.addr);
1678 memset(&iface->config.dhcpv6.addr, 0, sizeof(struct in6_addr));
1679 rediscover = true;
1680 goto prefix;
1681 }
1682
1683 /* TODO On nobiding (renew/rebind) go to requesting */
1684
1685 if (!net_ipv6_addr_cmp(&iface->config.dhcpv6.addr,
1686 net_ipv6_unspecified_address()) &&
1687 !net_ipv6_addr_cmp(&iface->config.dhcpv6.addr,
1688 &ia_na.iaaddr.addr)) {
1689 /* Remove old lease. */
1690 net_if_ipv6_addr_rm(iface, &iface->config.dhcpv6.addr);
1691 }
1692
1693 memcpy(&iface->config.dhcpv6.addr, &ia_na.iaaddr.addr,
1694 sizeof(iface->config.dhcpv6.addr));
1695
1696 dhcvp6_update_deadlines(iface, now, ia_na.t1, ia_na.t2,
1697 ia_na.iaaddr.preferred_lifetime,
1698 ia_na.iaaddr.valid_lifetime);
1699
1700 ifaddr = net_if_ipv6_addr_lookup_by_iface(iface, &ia_na.iaaddr.addr);
1701 if (ifaddr != NULL) {
1702 net_if_ipv6_addr_update_lifetime(
1703 ifaddr, ia_na.iaaddr.valid_lifetime);
1704 } else if (net_if_ipv6_addr_add(iface, &ia_na.iaaddr.addr, NET_ADDR_DHCP,
1705 ia_na.iaaddr.valid_lifetime) == NULL) {
1706 NET_ERR("Failed to configure DHCPv6 address");
1707 net_dhcpv6_stop(iface);
1708 return -EFAULT;
1709 }
1710 }
1711
1712 prefix:
1713 if (iface->config.dhcpv6.params.request_prefix) {
1714 struct net_if_ipv6_prefix *ifprefix;
1715
1716 if (ia_pd.status == DHCPV6_STATUS_NO_PREFIX_AVAIL ||
1717 ia_pd.iaprefix.status == DHCPV6_STATUS_NO_PREFIX_AVAIL ||
1718 ia_pd.iaprefix.valid_lifetime == 0) {
1719 /* Remove old lease. */
1720 net_if_ipv6_prefix_rm(iface, &iface->config.dhcpv6.prefix,
1721 iface->config.dhcpv6.prefix_len);
1722 memset(&iface->config.dhcpv6.prefix, 0, sizeof(struct in6_addr));
1723 iface->config.dhcpv6.prefix_len = 0;
1724 rediscover = true;
1725 goto out;
1726 }
1727
1728 if (!net_ipv6_addr_cmp(&iface->config.dhcpv6.prefix,
1729 net_ipv6_unspecified_address()) &&
1730 (!net_ipv6_addr_cmp(&iface->config.dhcpv6.prefix,
1731 &ia_pd.iaprefix.prefix) ||
1732 iface->config.dhcpv6.prefix_len != ia_pd.iaprefix.prefix_len)) {
1733 /* Remove old lease. */
1734 net_if_ipv6_prefix_rm(iface, &iface->config.dhcpv6.prefix,
1735 iface->config.dhcpv6.prefix_len);
1736 }
1737
1738 iface->config.dhcpv6.prefix_len = ia_pd.iaprefix.prefix_len;
1739
1740 memcpy(&iface->config.dhcpv6.prefix, &ia_pd.iaprefix.prefix,
1741 sizeof(iface->config.dhcpv6.prefix));
1742
1743 dhcvp6_update_deadlines(iface, now, ia_pd.t1, ia_pd.t2,
1744 ia_pd.iaprefix.preferred_lifetime,
1745 ia_pd.iaprefix.valid_lifetime);
1746
1747 ifprefix = net_if_ipv6_prefix_lookup(iface, &ia_pd.iaprefix.prefix,
1748 ia_pd.iaprefix.prefix_len);
1749 if (ifprefix != NULL) {
1750 net_if_ipv6_prefix_set_timer(ifprefix, ia_pd.iaprefix.valid_lifetime);
1751 } else if (net_if_ipv6_prefix_add(iface, &ia_pd.iaprefix.prefix,
1752 ia_pd.iaprefix.prefix_len,
1753 ia_pd.iaprefix.valid_lifetime) == NULL) {
1754 NET_ERR("Failed to configure DHCPv6 prefix");
1755 net_dhcpv6_stop(iface);
1756 return -EFAULT;
1757 }
1758 }
1759
1760 out:
1761 if (rediscover) {
1762 dhcpv6_enter_state(iface, NET_DHCPV6_SOLICITING);
1763 } else {
1764 dhcpv6_enter_state(iface, NET_DHCPV6_BOUND);
1765 }
1766
1767 dhcpv6_reschedule();
1768
1769 return 0;
1770 }
1771
dhcpv6_handle_reconfigure(struct net_if * iface,struct net_pkt * pkt)1772 static int dhcpv6_handle_reconfigure(struct net_if *iface, struct net_pkt *pkt)
1773 {
1774 /* Reconfigure not supported yet. */
1775 return -ENOTSUP;
1776 }
1777
dhcpv6_input(struct net_conn * conn,struct net_pkt * pkt,union net_ip_header * ip_hdr,union net_proto_header * proto_hdr,void * user_data)1778 static enum net_verdict dhcpv6_input(struct net_conn *conn,
1779 struct net_pkt *pkt,
1780 union net_ip_header *ip_hdr,
1781 union net_proto_header *proto_hdr,
1782 void *user_data)
1783 {
1784 struct net_if *iface;
1785 uint8_t msg_type;
1786 uint8_t tid[DHCPV6_TID_SIZE];
1787 int ret;
1788
1789 if (!conn) {
1790 NET_ERR("Invalid connection");
1791 return NET_DROP;
1792 }
1793
1794 if (!pkt) {
1795 NET_ERR("Invalid packet");
1796 return NET_DROP;
1797 }
1798
1799 iface = net_pkt_iface(pkt);
1800 if (!iface) {
1801 NET_ERR("No interface");
1802 return NET_DROP;
1803 }
1804
1805 net_pkt_cursor_init(pkt);
1806
1807 if (net_pkt_skip(pkt, NET_IPV6UDPH_LEN)) {
1808 NET_ERR("Missing IPv6/UDP header");
1809 return NET_DROP;
1810 }
1811
1812 if (net_pkt_read_u8(pkt, &msg_type) < 0) {
1813 NET_ERR("Missing message type");
1814 return NET_DROP;
1815 }
1816
1817 if (net_pkt_read(pkt, tid, sizeof(tid)) < 0) {
1818 NET_ERR("Missing transaction ID");
1819 return NET_DROP;
1820 }
1821
1822 NET_DBG("Received DHCPv6 packet [type=%d, tid=0x%02x%02x%02x]",
1823 msg_type, tid[0], tid[1], tid[2]);
1824
1825 switch (msg_type) {
1826 case DHCPV6_MSG_TYPE_ADVERTISE:
1827 ret = dhcpv6_handle_advertise(iface, pkt, tid);
1828 break;
1829 case DHCPV6_MSG_TYPE_REPLY:
1830 ret = dhcpv6_handle_reply(iface, pkt, tid);
1831 break;
1832 case DHCPV6_MSG_TYPE_RECONFIGURE:
1833 ret = dhcpv6_handle_reconfigure(iface, pkt);
1834 break;
1835 case DHCPV6_MSG_TYPE_SOLICIT:
1836 case DHCPV6_MSG_TYPE_REQUEST:
1837 case DHCPV6_MSG_TYPE_CONFIRM:
1838 case DHCPV6_MSG_TYPE_RENEW:
1839 case DHCPV6_MSG_TYPE_REBIND:
1840 case DHCPV6_MSG_TYPE_RELEASE:
1841 case DHCPV6_MSG_TYPE_DECLINE:
1842 case DHCPV6_MSG_TYPE_INFORMATION_REQUEST:
1843 case DHCPV6_MSG_TYPE_RELAY_FORW:
1844 case DHCPV6_MSG_TYPE_RELAY_REPL:
1845 default:
1846 goto drop;
1847 }
1848
1849 if (ret < 0) {
1850 goto drop;
1851 }
1852
1853 net_pkt_unref(pkt);
1854
1855 return NET_OK;
1856
1857 drop:
1858 return NET_DROP;
1859 }
1860
1861 /* DHCPv6 timer management */
1862
dhcpv6_timeleft(struct net_if * iface,int64_t now)1863 static uint64_t dhcpv6_timeleft(struct net_if *iface, int64_t now)
1864 {
1865 uint64_t timeout = iface->config.dhcpv6.timeout;
1866
1867 if (timeout > now) {
1868 return timeout - now;
1869 }
1870
1871 return 0;
1872 }
1873
dhcpv6_manage_timers(struct net_if * iface,int64_t now)1874 static uint64_t dhcpv6_manage_timers(struct net_if *iface, int64_t now)
1875 {
1876 uint64_t timeleft = dhcpv6_timeleft(iface, now);
1877
1878 NET_DBG("iface %p state=%s timeleft=%llu", iface,
1879 net_dhcpv6_state_name(iface->config.dhcpv6.state), timeleft);
1880
1881 if (timeleft != 0U) {
1882 return iface->config.dhcpv6.timeout;
1883 }
1884
1885 if (!net_if_is_up(iface)) {
1886 /* An interface is down, the registered event handler will
1887 * restart DHCP procedure when the interface is back up.
1888 */
1889 return UINT64_MAX;
1890 }
1891
1892 switch (iface->config.dhcpv6.state) {
1893 case NET_DHCPV6_DISABLED:
1894 break;
1895 case NET_DHCPV6_INIT: {
1896 bool have_addr = false;
1897 bool have_prefix = false;
1898
1899 if (iface->config.dhcpv6.params.request_addr &&
1900 !net_ipv6_addr_cmp(&iface->config.dhcpv6.addr,
1901 net_ipv6_unspecified_address())) {
1902 have_addr = true;
1903 }
1904
1905 if (iface->config.dhcpv6.params.request_prefix &&
1906 !net_ipv6_addr_cmp(&iface->config.dhcpv6.prefix,
1907 net_ipv6_unspecified_address())) {
1908 have_prefix = true;
1909 }
1910
1911 if ((have_addr || have_prefix) && now < iface->config.dhcpv6.expire) {
1912 /* Try to confirm the address/prefix. In case
1913 * prefix is requested, Rebind is used with
1914 * Confirm timings.
1915 */
1916 iface->config.dhcpv6.expire = now + DHCPV6_CNF_MAX_RD;
1917
1918 if (!iface->config.dhcpv6.params.request_prefix) {
1919 dhcpv6_enter_state(iface, NET_DHCPV6_CONFIRMING);
1920 } else {
1921 dhcpv6_enter_state(iface, NET_DHCPV6_REBINDING);
1922 }
1923 } else {
1924 dhcpv6_enter_state(iface, NET_DHCPV6_SOLICITING);
1925 }
1926
1927 return iface->config.dhcpv6.timeout;
1928 }
1929 case NET_DHCPV6_SOLICITING:
1930 if (iface->config.dhcpv6.server_preference >= 0) {
1931 dhcpv6_enter_state(iface, NET_DHCPV6_REQUESTING);
1932 return iface->config.dhcpv6.timeout;
1933 }
1934
1935 iface->config.dhcpv6.retransmissions++;
1936 iface->config.dhcpv6.retransmit_timeout =
1937 dhcpv6_next_retransmit_time(
1938 iface->config.dhcpv6.retransmit_timeout,
1939 DHCPV6_SOL_MAX_RT);
1940
1941 (void)dhcpv6_send_solicit(iface);
1942 dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout);
1943
1944 return iface->config.dhcpv6.timeout;
1945 case NET_DHCPV6_REQUESTING:
1946 if (iface->config.dhcpv6.retransmissions >= DHCPV6_REQ_MAX_RC) {
1947 /* Back to soliciting. */
1948 dhcpv6_enter_state(iface, NET_DHCPV6_SOLICITING);
1949 return iface->config.dhcpv6.timeout;
1950 }
1951
1952 iface->config.dhcpv6.retransmissions++;
1953 iface->config.dhcpv6.retransmit_timeout =
1954 dhcpv6_next_retransmit_time(
1955 iface->config.dhcpv6.retransmit_timeout,
1956 DHCPV6_REQ_MAX_RT);
1957
1958 (void)dhcpv6_send_request(iface);
1959 dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout);
1960
1961 return iface->config.dhcpv6.timeout;
1962 case NET_DHCPV6_CONFIRMING:
1963 if (now >= iface->config.dhcpv6.expire) {
1964 dhcpv6_enter_state(iface, NET_DHCPV6_SOLICITING);
1965 return iface->config.dhcpv6.timeout;
1966 }
1967
1968 iface->config.dhcpv6.retransmissions++;
1969 iface->config.dhcpv6.retransmit_timeout =
1970 dhcpv6_next_retransmit_time(
1971 iface->config.dhcpv6.retransmit_timeout,
1972 DHCPV6_CNF_MAX_RT);
1973
1974 (void)dhcpv6_send_confirm(iface);
1975 dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout);
1976
1977 if (iface->config.dhcpv6.timeout > iface->config.dhcpv6.expire) {
1978 iface->config.dhcpv6.timeout = iface->config.dhcpv6.expire;
1979 }
1980
1981 return iface->config.dhcpv6.timeout;
1982 case NET_DHCPV6_RENEWING:
1983 if (now >= iface->config.dhcpv6.t2) {
1984 dhcpv6_enter_state(iface, NET_DHCPV6_REBINDING);
1985 return iface->config.dhcpv6.timeout;
1986 }
1987
1988 iface->config.dhcpv6.retransmissions++;
1989 iface->config.dhcpv6.retransmit_timeout =
1990 dhcpv6_next_retransmit_time(
1991 iface->config.dhcpv6.retransmit_timeout,
1992 DHCPV6_REN_MAX_RT);
1993
1994 (void)dhcpv6_send_renew(iface);
1995 dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout);
1996
1997 if (iface->config.dhcpv6.timeout > iface->config.dhcpv6.t2) {
1998 iface->config.dhcpv6.timeout = iface->config.dhcpv6.t2;
1999 }
2000
2001 return iface->config.dhcpv6.timeout;
2002 case NET_DHCPV6_REBINDING:
2003 if (now >= iface->config.dhcpv6.expire) {
2004 dhcpv6_enter_state(iface, NET_DHCPV6_SOLICITING);
2005 return iface->config.dhcpv6.timeout;
2006 }
2007
2008 iface->config.dhcpv6.retransmissions++;
2009 iface->config.dhcpv6.retransmit_timeout =
2010 dhcpv6_next_retransmit_time(
2011 iface->config.dhcpv6.retransmit_timeout,
2012 DHCPV6_REB_MAX_RT);
2013
2014 (void)dhcpv6_send_rebind(iface);
2015 dhcpv6_set_timeout(iface, iface->config.dhcpv6.retransmit_timeout);
2016
2017 if (iface->config.dhcpv6.timeout > iface->config.dhcpv6.expire) {
2018 iface->config.dhcpv6.timeout = iface->config.dhcpv6.expire;
2019 }
2020
2021 return iface->config.dhcpv6.timeout;
2022 case NET_DHCPV6_INFO_REQUESTING:
2023 break;
2024 case NET_DHCPV6_BOUND:
2025 dhcpv6_enter_state(iface, NET_DHCPV6_RENEWING);
2026 return iface->config.dhcpv6.timeout;
2027 }
2028
2029 return UINT64_MAX;
2030 }
2031
dhcpv6_timeout(struct k_work * work)2032 static void dhcpv6_timeout(struct k_work *work)
2033 {
2034 uint64_t timeout_update = UINT64_MAX;
2035 int64_t now = k_uptime_get();
2036 struct net_if_dhcpv6 *current, *next;
2037
2038 ARG_UNUSED(work);
2039
2040 k_mutex_lock(&lock, K_FOREVER);
2041
2042 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&dhcpv6_ifaces, current, next, node) {
2043 struct net_if *iface = CONTAINER_OF(
2044 CONTAINER_OF(current, struct net_if_config, dhcpv6),
2045 struct net_if, config);
2046 uint64_t next_timeout;
2047
2048 next_timeout = dhcpv6_manage_timers(iface, now);
2049 if (next_timeout < timeout_update) {
2050 timeout_update = next_timeout;
2051 }
2052 }
2053
2054 k_mutex_unlock(&lock);
2055
2056 if (timeout_update != UINT64_MAX) {
2057 if (now > timeout_update) {
2058 timeout_update = 0ULL;
2059 } else {
2060 timeout_update -= now;
2061 }
2062
2063 NET_DBG("Waiting for %llums", timeout_update);
2064 k_work_reschedule(&dhcpv6_timeout_work, K_MSEC(timeout_update));
2065 }
2066 }
2067
dhcpv6_iface_event_handler(struct net_mgmt_event_callback * cb,uint32_t mgmt_event,struct net_if * iface)2068 static void dhcpv6_iface_event_handler(struct net_mgmt_event_callback *cb,
2069 uint32_t mgmt_event, struct net_if *iface)
2070 {
2071 sys_snode_t *node = NULL;
2072
2073 k_mutex_lock(&lock, K_FOREVER);
2074
2075 SYS_SLIST_FOR_EACH_NODE(&dhcpv6_ifaces, node) {
2076 if (node == &iface->config.dhcpv6.node) {
2077 break;
2078 }
2079 }
2080
2081 if (node == NULL) {
2082 goto out;
2083 }
2084
2085 if (mgmt_event == NET_EVENT_IF_DOWN) {
2086 NET_DBG("Interface %p going down", iface);
2087 dhcpv6_set_timeout(iface, UINT64_MAX);
2088 } else if (mgmt_event == NET_EVENT_IF_UP) {
2089 NET_DBG("Interface %p coming up", iface);
2090 dhcpv6_enter_state(iface, NET_DHCPV6_INIT);
2091 }
2092
2093 dhcpv6_reschedule();
2094
2095 out:
2096 k_mutex_unlock(&lock);
2097 }
2098
dhcpv6_generate_client_duid(struct net_if * iface)2099 static void dhcpv6_generate_client_duid(struct net_if *iface)
2100 {
2101 struct net_linkaddr *lladdr = net_if_get_link_addr(iface);
2102 struct net_dhcpv6_duid_storage *clientid = &iface->config.dhcpv6.clientid;
2103 struct dhcpv6_duid_ll *duid_ll =
2104 (struct dhcpv6_duid_ll *)&clientid->duid.buf;
2105
2106 memset(clientid, 0, sizeof(*clientid));
2107
2108 UNALIGNED_PUT(htons(DHCPV6_DUID_TYPE_LL), &clientid->duid.type);
2109 UNALIGNED_PUT(htons(DHCPV6_HARDWARE_ETHERNET_TYPE), &duid_ll->hw_type);
2110 memcpy(duid_ll->ll_addr, lladdr->addr, lladdr->len);
2111
2112 clientid->length = DHCPV6_DUID_LL_HEADER_SIZE + lladdr->len;
2113 }
2114
2115 /* DHCPv6 public API */
2116
net_dhcpv6_start(struct net_if * iface,struct net_dhcpv6_params * params)2117 void net_dhcpv6_start(struct net_if *iface, struct net_dhcpv6_params *params)
2118 {
2119 k_mutex_lock(&lock, K_FOREVER);
2120
2121 if (iface->config.dhcpv6.state != NET_DHCPV6_DISABLED) {
2122 NET_ERR("DHCPv6 already running on iface %p, state %s", iface,
2123 net_dhcpv6_state_name(iface->config.dhcpv6.state));
2124 goto out;
2125 }
2126
2127 if (!params->request_addr && !params->request_prefix) {
2128 NET_ERR("Information Request not supported yet");
2129 goto out;
2130 }
2131
2132 net_mgmt_event_notify(NET_EVENT_IPV6_DHCP_START, iface);
2133
2134 NET_DBG("Starting DHCPv6 on iface %p", iface);
2135
2136 iface->config.dhcpv6.params = *params;
2137
2138 if (sys_slist_is_empty(&dhcpv6_ifaces)) {
2139 net_mgmt_add_event_callback(&dhcpv6_mgmt_cb);
2140 }
2141
2142 sys_slist_append(&dhcpv6_ifaces, &iface->config.dhcpv6.node);
2143
2144 if (params->request_addr) {
2145 iface->config.dhcpv6.addr_iaid = net_if_get_by_iface(iface);
2146 }
2147
2148 if (params->request_prefix) {
2149 iface->config.dhcpv6.prefix_iaid = net_if_get_by_iface(iface);
2150 }
2151
2152 dhcpv6_generate_client_duid(iface);
2153 dhcpv6_enter_state(iface, NET_DHCPV6_INIT);
2154 dhcpv6_reschedule();
2155
2156 out:
2157 k_mutex_unlock(&lock);
2158 }
2159
net_dhcpv6_stop(struct net_if * iface)2160 void net_dhcpv6_stop(struct net_if *iface)
2161 {
2162 k_mutex_lock(&lock, K_FOREVER);
2163
2164 switch (iface->config.dhcpv6.state) {
2165 case NET_DHCPV6_DISABLED:
2166 NET_INFO("DHCPv6 already disabled on iface %p", iface);
2167 break;
2168
2169 case NET_DHCPV6_INIT:
2170 case NET_DHCPV6_SOLICITING:
2171 case NET_DHCPV6_REQUESTING:
2172 case NET_DHCPV6_CONFIRMING:
2173 case NET_DHCPV6_RENEWING:
2174 case NET_DHCPV6_REBINDING:
2175 case NET_DHCPV6_INFO_REQUESTING:
2176 case NET_DHCPV6_BOUND:
2177 NET_DBG("Stopping DHCPv6 on iface %p, state %s", iface,
2178 net_dhcpv6_state_name(iface->config.dhcpv6.state));
2179
2180 (void)dhcpv6_enter_state(iface, NET_DHCPV6_DISABLED);
2181
2182 sys_slist_find_and_remove(&dhcpv6_ifaces,
2183 &iface->config.dhcpv6.node);
2184
2185 if (sys_slist_is_empty(&dhcpv6_ifaces)) {
2186 (void)k_work_cancel_delayable(&dhcpv6_timeout_work);
2187 net_mgmt_del_event_callback(&dhcpv6_mgmt_cb);
2188 }
2189
2190 break;
2191 }
2192
2193 net_mgmt_event_notify(NET_EVENT_IPV6_DHCP_STOP, iface);
2194
2195 k_mutex_unlock(&lock);
2196 }
2197
net_dhcpv6_restart(struct net_if * iface)2198 void net_dhcpv6_restart(struct net_if *iface)
2199 {
2200 struct net_dhcpv6_params params = iface->config.dhcpv6.params;
2201
2202 net_dhcpv6_stop(iface);
2203 net_dhcpv6_start(iface, ¶ms);
2204 }
2205
net_dhcpv6_init(void)2206 int net_dhcpv6_init(void)
2207 {
2208 struct sockaddr unspec_addr;
2209 int ret;
2210
2211 net_ipaddr_copy(&net_sin6(&unspec_addr)->sin6_addr,
2212 net_ipv6_unspecified_address());
2213 unspec_addr.sa_family = AF_INET6;
2214
2215 ret = net_udp_register(AF_INET6, NULL, &unspec_addr,
2216 DHCPV6_SERVER_PORT, DHCPV6_CLIENT_PORT,
2217 NULL, dhcpv6_input, NULL, NULL);
2218 if (ret < 0) {
2219 NET_DBG("UDP callback registration failed");
2220 return ret;
2221 }
2222
2223 k_work_init_delayable(&dhcpv6_timeout_work, dhcpv6_timeout);
2224 net_mgmt_init_event_callback(&dhcpv6_mgmt_cb, dhcpv6_iface_event_handler,
2225 NET_EVENT_IF_DOWN | NET_EVENT_IF_UP);
2226
2227 return 0;
2228 }
2229