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