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