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, &params);
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