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