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