1 /** @file
2  * @brief IPv6 Fragment related functions
3  */
4 
5 /*
6  * Copyright (c) 2018 Intel Corporation
7  *
8  * SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_DECLARE(net_ipv6, CONFIG_NET_IPV6_LOG_LEVEL);
13 
14 #include <errno.h>
15 #include <zephyr/net/net_core.h>
16 #include <zephyr/net/net_pkt.h>
17 #include <zephyr/net/net_stats.h>
18 #include <zephyr/net/net_context.h>
19 #include <zephyr/net/net_mgmt.h>
20 #include <zephyr/random/random.h>
21 #include "net_private.h"
22 #include "connection.h"
23 #include "icmpv6.h"
24 #include "udp_internal.h"
25 #include "tcp_internal.h"
26 #include "ipv6.h"
27 #include "nbr.h"
28 #include "6lo.h"
29 #include "route.h"
30 #include "net_stats.h"
31 
32 /* Timeout for various buffer allocations in this file. */
33 #define NET_BUF_TIMEOUT K_MSEC(50)
34 
35 #if defined(CONFIG_NET_IPV6_FRAGMENT_TIMEOUT)
36 #define IPV6_REASSEMBLY_TIMEOUT K_SECONDS(CONFIG_NET_IPV6_FRAGMENT_TIMEOUT)
37 #else
38 #define IPV6_REASSEMBLY_TIMEOUT K_SECONDS(5)
39 #endif /* CONFIG_NET_IPV6_FRAGMENT_TIMEOUT */
40 
41 #define FRAG_BUF_WAIT K_MSEC(10) /* how long to max wait for a buffer */
42 
43 static void reassembly_timeout(struct k_work *work);
44 static bool reassembly_init_done;
45 
46 static struct net_ipv6_reassembly
47 reassembly[CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT];
48 
net_ipv6_find_last_ext_hdr(struct net_pkt * pkt,uint16_t * next_hdr_off,uint16_t * last_hdr_off)49 int net_ipv6_find_last_ext_hdr(struct net_pkt *pkt, uint16_t *next_hdr_off,
50 			       uint16_t *last_hdr_off)
51 {
52 	NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr);
53 	struct net_ipv6_hdr *hdr;
54 	uint8_t next_nexthdr;
55 	uint8_t nexthdr;
56 
57 	if (!pkt || !pkt->frags || !next_hdr_off || !last_hdr_off) {
58 		return -EINVAL;
59 	}
60 
61 	net_pkt_cursor_init(pkt);
62 
63 	hdr = (struct net_ipv6_hdr *)net_pkt_get_data(pkt, &ipv6_access);
64 	if (!hdr) {
65 		return -ENOBUFS;
66 	}
67 
68 	net_pkt_acknowledge_data(pkt, &ipv6_access);
69 
70 	nexthdr = hdr->nexthdr;
71 
72 	/* Initial values */
73 	*next_hdr_off = offsetof(struct net_ipv6_hdr, nexthdr);
74 	*last_hdr_off = sizeof(struct net_ipv6_hdr);
75 
76 	while (!net_ipv6_is_nexthdr_upper_layer(nexthdr)) {
77 		if (net_pkt_read_u8(pkt, &next_nexthdr)) {
78 			goto fail;
79 		}
80 
81 		switch (nexthdr) {
82 		case NET_IPV6_NEXTHDR_HBHO:
83 		case NET_IPV6_NEXTHDR_DESTO:
84 			{
85 				uint8_t val = 0U;
86 				uint16_t length;
87 
88 				if (net_pkt_read_u8(pkt, &val)) {
89 					goto fail;
90 				}
91 
92 				length = val * 8U + 8 - 2;
93 
94 				if (net_pkt_skip(pkt, length)) {
95 					goto fail;
96 				}
97 			}
98 			break;
99 		case NET_IPV6_NEXTHDR_FRAG:
100 			if (net_pkt_skip(pkt, 7)) {
101 				goto fail;
102 			}
103 
104 			break;
105 		case NET_IPV6_NEXTHDR_NONE:
106 			goto out;
107 		default:
108 			/* TODO: Add more IPv6 extension headers to check */
109 			goto fail;
110 		}
111 
112 		*next_hdr_off = *last_hdr_off;
113 		*last_hdr_off = net_pkt_get_current_offset(pkt);
114 
115 		nexthdr = next_nexthdr;
116 	}
117 out:
118 	return 0;
119 fail:
120 	return -EINVAL;
121 }
122 
reassembly_get(uint32_t id,const uint8_t * src,const uint8_t * dst)123 static struct net_ipv6_reassembly *reassembly_get(uint32_t id,
124 						  const uint8_t *src,
125 						  const uint8_t *dst)
126 {
127 	int i, avail = -1;
128 
129 	for (i = 0; i < CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT; i++) {
130 		if (k_work_delayable_remaining_get(&reassembly[i].timer) &&
131 		    reassembly[i].id == id &&
132 		    net_ipv6_addr_cmp_raw(src, reassembly[i].src.s6_addr) &&
133 		    net_ipv6_addr_cmp_raw(dst, reassembly[i].dst.s6_addr)) {
134 			return &reassembly[i];
135 		}
136 
137 		if (k_work_delayable_remaining_get(&reassembly[i].timer)) {
138 			continue;
139 		}
140 
141 		if (avail < 0) {
142 			avail = i;
143 		}
144 	}
145 
146 	if (avail < 0) {
147 		return NULL;
148 	}
149 
150 	k_work_reschedule(&reassembly[avail].timer, IPV6_REASSEMBLY_TIMEOUT);
151 
152 	net_ipv6_addr_copy_raw(reassembly[avail].src.s6_addr, src);
153 	net_ipv6_addr_copy_raw(reassembly[avail].dst.s6_addr, dst);
154 
155 	reassembly[avail].id = id;
156 
157 	return &reassembly[avail];
158 }
159 
reassembly_cancel(uint32_t id,struct in6_addr * src,struct in6_addr * dst)160 static bool reassembly_cancel(uint32_t id,
161 			      struct in6_addr *src,
162 			      struct in6_addr *dst)
163 {
164 	int i, j;
165 
166 	NET_DBG("Cancel 0x%x", id);
167 
168 	for (i = 0; i < CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT; i++) {
169 		int32_t remaining;
170 
171 		if (reassembly[i].id != id ||
172 		    !net_ipv6_addr_cmp(src, &reassembly[i].src) ||
173 		    !net_ipv6_addr_cmp(dst, &reassembly[i].dst)) {
174 			continue;
175 		}
176 
177 		remaining = k_ticks_to_ms_ceil32(
178 			k_work_delayable_remaining_get(&reassembly[i].timer));
179 		k_work_cancel_delayable(&reassembly[i].timer);
180 
181 		NET_DBG("IPv6 reassembly id 0x%x remaining %d ms",
182 			reassembly[i].id, remaining);
183 
184 		reassembly[i].id = 0U;
185 
186 		for (j = 0; j < CONFIG_NET_IPV6_FRAGMENT_MAX_PKT; j++) {
187 			if (!reassembly[i].pkt[j]) {
188 				continue;
189 			}
190 
191 			NET_DBG("[%d] IPv6 reassembly pkt %p %zd bytes data",
192 				j, reassembly[i].pkt[j],
193 				net_pkt_get_len(reassembly[i].pkt[j]));
194 
195 			net_pkt_unref(reassembly[i].pkt[j]);
196 			reassembly[i].pkt[j] = NULL;
197 		}
198 
199 		return true;
200 	}
201 
202 	return false;
203 }
204 
reassembly_info(char * str,struct net_ipv6_reassembly * reass)205 static void reassembly_info(char *str, struct net_ipv6_reassembly *reass)
206 {
207 	NET_DBG("%s id 0x%x src %s dst %s remain %d ms", str, reass->id,
208 		net_sprint_ipv6_addr(&reass->src),
209 		net_sprint_ipv6_addr(&reass->dst),
210 		k_ticks_to_ms_ceil32(
211 			k_work_delayable_remaining_get(&reass->timer)));
212 }
213 
reassembly_timeout(struct k_work * work)214 static void reassembly_timeout(struct k_work *work)
215 {
216 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
217 	struct net_ipv6_reassembly *reass =
218 		CONTAINER_OF(dwork, struct net_ipv6_reassembly, timer);
219 
220 	reassembly_info("Reassembly cancelled", reass);
221 
222 	/* Send a ICMPv6 Time Exceeded only if we received the first fragment (RFC 2460 Sec. 5) */
223 	if (reass->pkt[0] && net_pkt_ipv6_fragment_offset(reass->pkt[0]) == 0) {
224 		net_icmpv6_send_error(reass->pkt[0], NET_ICMPV6_TIME_EXCEEDED, 1, 0);
225 	}
226 
227 	reassembly_cancel(reass->id, &reass->src, &reass->dst);
228 }
229 
reassemble_packet(struct net_ipv6_reassembly * reass)230 static void reassemble_packet(struct net_ipv6_reassembly *reass)
231 {
232 	NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr);
233 	NET_PKT_DATA_ACCESS_DEFINE(frag_access, struct net_ipv6_frag_hdr);
234 	union {
235 		struct net_ipv6_hdr *hdr;
236 		struct net_ipv6_frag_hdr *frag_hdr;
237 	} ipv6;
238 
239 	struct net_pkt *pkt;
240 	struct net_buf *last;
241 	uint8_t next_hdr;
242 	int i, len;
243 
244 	k_work_cancel_delayable(&reass->timer);
245 
246 	NET_ASSERT(reass->pkt[0]);
247 
248 	last = net_buf_frag_last(reass->pkt[0]->buffer);
249 
250 	/* We start from 2nd packet which is then appended to
251 	 * the first one.
252 	 */
253 	for (i = 1; i < CONFIG_NET_IPV6_FRAGMENT_MAX_PKT; i++) {
254 		int removed_len;
255 
256 		pkt = reass->pkt[i];
257 		if (!pkt) {
258 			break;
259 		}
260 
261 		net_pkt_cursor_init(pkt);
262 
263 		/* Get rid of IPv6 and fragment header which are at
264 		 * the beginning of the fragment.
265 		 */
266 		removed_len = net_pkt_ipv6_fragment_start(pkt) +
267 			      sizeof(struct net_ipv6_frag_hdr);
268 
269 		NET_DBG("Removing %d bytes from start of pkt %p",
270 			removed_len, pkt->buffer);
271 
272 		if (net_pkt_pull(pkt, removed_len)) {
273 			NET_ERR("Failed to pull headers");
274 			reassembly_cancel(reass->id, &reass->src, &reass->dst);
275 			return;
276 		}
277 
278 		/* Attach the data to previous pkt */
279 		last->frags = pkt->buffer;
280 		last = net_buf_frag_last(pkt->buffer);
281 
282 		pkt->buffer = NULL;
283 		reass->pkt[i] = NULL;
284 
285 		net_pkt_unref(pkt);
286 	}
287 
288 	pkt = reass->pkt[0];
289 	reass->pkt[0] = NULL;
290 
291 	/* Next we need to strip away the fragment header from the first packet
292 	 * and set the various pointers and values in packet.
293 	 */
294 	net_pkt_cursor_init(pkt);
295 
296 	if (net_pkt_skip(pkt, net_pkt_ipv6_fragment_start(pkt))) {
297 		NET_ERR("Failed to move to fragment header");
298 		goto error;
299 	}
300 
301 	ipv6.frag_hdr = (struct net_ipv6_frag_hdr *)net_pkt_get_data(
302 							pkt, &frag_access);
303 	if (!ipv6.frag_hdr) {
304 		NET_ERR("Failed to get fragment header");
305 		goto error;
306 	}
307 
308 	next_hdr = ipv6.frag_hdr->nexthdr;
309 
310 	if (net_pkt_pull(pkt, sizeof(struct net_ipv6_frag_hdr))) {
311 		NET_ERR("Failed to remove fragment header");
312 		goto error;
313 	}
314 
315 	/* This one updates the previous header's nexthdr value */
316 	if (net_pkt_skip(pkt, net_pkt_ipv6_hdr_prev(pkt)) ||
317 	    net_pkt_write_u8(pkt, next_hdr)) {
318 		goto error;
319 	}
320 
321 	net_pkt_cursor_init(pkt);
322 
323 	ipv6.hdr = (struct net_ipv6_hdr *)net_pkt_get_data(pkt, &ipv6_access);
324 	if (!ipv6.hdr) {
325 		goto error;
326 	}
327 
328 	/* Fix the total length of the IPv6 packet. */
329 	len = net_pkt_ipv6_ext_len(pkt);
330 	if (len > 0) {
331 		NET_DBG("Old pkt %p IPv6 ext len is %d bytes", pkt, len);
332 		net_pkt_set_ipv6_ext_len(pkt,
333 				len - sizeof(struct net_ipv6_frag_hdr));
334 	}
335 
336 	len = net_pkt_get_len(pkt) - sizeof(struct net_ipv6_hdr);
337 
338 	ipv6.hdr->len = htons(len);
339 
340 	net_pkt_set_data(pkt, &ipv6_access);
341 	net_pkt_set_ip_reassembled(pkt, true);
342 
343 	NET_DBG("New pkt %p IPv6 len is %d bytes", pkt,
344 		len + NET_IPV6H_LEN);
345 
346 	/* We need to use the queue when feeding the packet back into the
347 	 * IP stack as we might run out of stack if we call processing_data()
348 	 * directly. As the packet does not contain link layer header, we
349 	 * MUST NOT pass it to L2 so there will be a special check for that
350 	 * in process_data() when handling the packet.
351 	 */
352 	if (net_recv_data(net_pkt_iface(pkt), pkt) >= 0) {
353 		return;
354 	}
355 error:
356 	net_pkt_unref(pkt);
357 }
358 
net_ipv6_frag_foreach(net_ipv6_frag_cb_t cb,void * user_data)359 void net_ipv6_frag_foreach(net_ipv6_frag_cb_t cb, void *user_data)
360 {
361 	int i;
362 
363 	for (i = 0; reassembly_init_done &&
364 		     i < CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT; i++) {
365 		if (!k_work_delayable_remaining_get(&reassembly[i].timer)) {
366 			continue;
367 		}
368 
369 		cb(&reassembly[i], user_data);
370 	}
371 }
372 
373 /* Verify that we have all the fragments received and in correct order.
374  * Return:
375  * - a negative value if the fragments are erroneous and must be dropped
376  * - zero if we are expecting more fragments
377  * - a positive value if we can proceed with the reassembly
378  */
fragments_are_ready(struct net_ipv6_reassembly * reass)379 static int fragments_are_ready(struct net_ipv6_reassembly *reass)
380 {
381 	unsigned int expected_offset = 0;
382 	bool more = true;
383 	int i;
384 
385 	/* Fragments can arrive in any order, for example in reverse order:
386 	 *   1 -> Fragment3(M=0, offset=x2)
387 	 *   2 -> Fragment2(M=1, offset=x1)
388 	 *   3 -> Fragment1(M=1, offset=0)
389 	 * We have to test several requirements before proceeding with the reassembly:
390 	 * - We received the first fragment (Fragment Offset is 0)
391 	 * - All intermediate fragments are contiguous
392 	 * - The More bit of the last fragment is 0
393 	 */
394 	for (i = 0; i < CONFIG_NET_IPV6_FRAGMENT_MAX_PKT; i++) {
395 		struct net_pkt *pkt = reass->pkt[i];
396 		unsigned int offset;
397 		int payload_len;
398 
399 		if (!pkt) {
400 			break;
401 		}
402 
403 		offset = net_pkt_ipv6_fragment_offset(pkt);
404 
405 		if (offset < expected_offset) {
406 			/* Overlapping or duplicated
407 			 * According to RFC8200 we can drop it
408 			 */
409 			return -EBADMSG;
410 		} else if (offset != expected_offset) {
411 			/* Not contiguous, let's wait for fragments */
412 			return 0;
413 		}
414 
415 		payload_len = net_pkt_get_len(pkt) - net_pkt_ipv6_fragment_start(pkt);
416 		payload_len -= sizeof(struct net_ipv6_frag_hdr);
417 		if (payload_len < 0) {
418 			return -EBADMSG;
419 		}
420 
421 		expected_offset += payload_len;
422 		more = net_pkt_ipv6_fragment_more(pkt);
423 	}
424 
425 	if (more) {
426 		return 0;
427 	}
428 
429 	return 1;
430 }
431 
shift_packets(struct net_ipv6_reassembly * reass,int pos)432 static int shift_packets(struct net_ipv6_reassembly *reass, int pos)
433 {
434 	int i;
435 
436 	for (i = pos + 1; i < CONFIG_NET_IPV6_FRAGMENT_MAX_PKT; i++) {
437 		if (!reass->pkt[i]) {
438 			NET_DBG("Moving [%d] %p (offset 0x%x) to [%d]",
439 				pos, reass->pkt[pos],
440 				net_pkt_ipv6_fragment_offset(reass->pkt[pos]),
441 				pos + 1);
442 
443 			/* pkt[i] is free, so shift everything between
444 			 * [pos] and [i - 1] by one element
445 			 */
446 			memmove(&reass->pkt[pos + 1], &reass->pkt[pos],
447 				sizeof(void *) * (i - pos));
448 
449 			/* pkt[pos] is now free */
450 			reass->pkt[pos] = NULL;
451 
452 			return 0;
453 		}
454 	}
455 
456 	/* We do not have free space left in the array */
457 	return -ENOMEM;
458 }
459 
net_ipv6_handle_fragment_hdr(struct net_pkt * pkt,struct net_ipv6_hdr * hdr,uint8_t nexthdr)460 enum net_verdict net_ipv6_handle_fragment_hdr(struct net_pkt *pkt,
461 					      struct net_ipv6_hdr *hdr,
462 					      uint8_t nexthdr)
463 {
464 	struct net_ipv6_reassembly *reass = NULL;
465 	uint16_t flag;
466 	bool found;
467 	uint8_t more;
468 	uint32_t id;
469 	int ret;
470 	int i;
471 
472 	if (!reassembly_init_done) {
473 		/* Static initializing does not work here because of the array
474 		 * so we must do it at runtime.
475 		 */
476 		for (i = 0; i < CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT; i++) {
477 			k_work_init_delayable(&reassembly[i].timer,
478 					      reassembly_timeout);
479 		}
480 
481 		reassembly_init_done = true;
482 	}
483 
484 	/* Each fragment has a fragment header, however since we already
485 	 * read the nexthdr part of it, we are not going to use
486 	 * net_pkt_get_data() and access the header directly: the cursor
487 	 * being 1 byte too far, let's just read the next relevant pieces.
488 	 */
489 	if (net_pkt_skip(pkt, 1) || /* reserved */
490 	    net_pkt_read_be16(pkt, &flag) ||
491 	    net_pkt_read_be32(pkt, &id)) {
492 		goto drop;
493 	}
494 
495 	reass = reassembly_get(id, hdr->src, hdr->dst);
496 	if (!reass) {
497 		NET_DBG("Cannot get reassembly slot, dropping pkt %p", pkt);
498 		goto drop;
499 	}
500 
501 	more = flag & 0x01;
502 	net_pkt_set_ipv6_fragment_flags(pkt, flag);
503 
504 	if (more && net_pkt_get_len(pkt) % 8) {
505 		/* Fragment length is not multiple of 8, discard
506 		 * the packet and send parameter problem error with the
507 		 * offset of the "Payload Length" field in the IPv6 header.
508 		 */
509 		net_icmpv6_send_error(pkt, NET_ICMPV6_PARAM_PROBLEM,
510 				      NET_ICMPV6_PARAM_PROB_HEADER, NET_IPV6H_LENGTH_OFFSET);
511 		goto drop;
512 	}
513 
514 	/* The fragments might come in wrong order so place them
515 	 * in reassembly chain in correct order.
516 	 */
517 	for (i = 0, found = false; i < CONFIG_NET_IPV6_FRAGMENT_MAX_PKT; i++) {
518 		if (reass->pkt[i]) {
519 			if (net_pkt_ipv6_fragment_offset(reass->pkt[i]) <
520 			    net_pkt_ipv6_fragment_offset(pkt)) {
521 				continue;
522 			}
523 
524 			/* Make room for this fragment. If there is no room,
525 			 * then it will discard the whole reassembly.
526 			 */
527 			if (shift_packets(reass, i)) {
528 				break;
529 			}
530 		}
531 
532 		NET_DBG("Storing pkt %p to slot %d offset %d",
533 			pkt, i, net_pkt_ipv6_fragment_offset(pkt));
534 		reass->pkt[i] = pkt;
535 		found = true;
536 
537 		break;
538 	}
539 
540 	if (!found) {
541 		/* We could not add this fragment into our saved fragment
542 		 * list. We must discard the whole packet at this point.
543 		 */
544 		NET_DBG("No slots available for 0x%x", reass->id);
545 		net_pkt_unref(pkt);
546 		goto drop;
547 	}
548 
549 	ret = fragments_are_ready(reass);
550 	if (ret < 0) {
551 		NET_DBG("Reassembled IPv6 verify failed, dropping id %u",
552 			reass->id);
553 
554 		/* Let the caller release the already inserted pkt */
555 		if (i < CONFIG_NET_IPV6_FRAGMENT_MAX_PKT) {
556 			reass->pkt[i] = NULL;
557 		}
558 
559 		net_pkt_unref(pkt);
560 		goto drop;
561 	} else if (ret == 0) {
562 		reassembly_info("Reassembly nth pkt", reass);
563 
564 		NET_DBG("More fragments to be received");
565 		goto accept;
566 	}
567 
568 	reassembly_info("Reassembly last pkt", reass);
569 
570 	/* The last fragment received, reassemble the packet */
571 	reassemble_packet(reass);
572 
573 accept:
574 	return NET_OK;
575 
576 drop:
577 	if (reass) {
578 		if (reassembly_cancel(reass->id, &reass->src, &reass->dst)) {
579 			return NET_OK;
580 		}
581 	}
582 
583 	return NET_DROP;
584 }
585 
586 #define BUF_ALLOC_TIMEOUT K_MSEC(100)
587 
send_ipv6_fragment(struct net_pkt * pkt,uint16_t fit_len,uint16_t frag_offset,uint16_t next_hdr_off,uint8_t next_hdr,bool final)588 static int send_ipv6_fragment(struct net_pkt *pkt,
589 			      uint16_t fit_len,
590 			      uint16_t frag_offset,
591 			      uint16_t next_hdr_off,
592 			      uint8_t next_hdr,
593 			      bool final)
594 {
595 	NET_PKT_DATA_ACCESS_DEFINE(frag_access, struct net_ipv6_frag_hdr);
596 	uint8_t frag_pkt_next_hdr = NET_IPV6_NEXTHDR_HBHO;
597 	int ret = -ENOBUFS;
598 	struct net_ipv6_frag_hdr *frag_hdr;
599 	struct net_pkt *frag_pkt;
600 
601 	frag_pkt = net_pkt_alloc_with_buffer(net_pkt_iface(pkt), fit_len +
602 					     net_pkt_ipv6_ext_len(pkt) +
603 					     NET_IPV6_FRAGH_LEN,
604 					     AF_INET6, 0, BUF_ALLOC_TIMEOUT);
605 	if (!frag_pkt) {
606 		return -ENOMEM;
607 	}
608 
609 	net_pkt_cursor_init(pkt);
610 
611 	net_pkt_set_ll_proto_type(frag_pkt, net_pkt_ll_proto_type(pkt));
612 
613 	/* We copy original headers back to the fragment packet
614 	 * Note that we insert the right next header to point to fragment header
615 	 */
616 	if (net_pkt_copy(frag_pkt, pkt, next_hdr_off) ||
617 	    net_pkt_write_u8(frag_pkt, NET_IPV6_NEXTHDR_FRAG) ||
618 	    net_pkt_skip(pkt, 1) ||
619 	    net_pkt_copy(frag_pkt, pkt, net_pkt_ip_hdr_len(pkt) +
620 			 net_pkt_ipv6_ext_len(pkt) - next_hdr_off - 1)) {
621 		goto fail;
622 	}
623 
624 	if (!net_pkt_ipv6_ext_len(pkt)) {
625 		frag_pkt_next_hdr = NET_IPV6_NEXTHDR_FRAG;
626 	}
627 
628 	/* And we append the fragmentation header */
629 	frag_hdr = (struct net_ipv6_frag_hdr *)net_pkt_get_data(frag_pkt,
630 								&frag_access);
631 	if (!frag_hdr) {
632 		goto fail;
633 	}
634 
635 	frag_hdr->nexthdr = next_hdr;
636 	frag_hdr->reserved = 0U;
637 	frag_hdr->id = net_pkt_ipv6_fragment_id(pkt);
638 	frag_hdr->offset = htons(((frag_offset / 8U) << 3) | !final);
639 
640 	net_pkt_set_chksum_done(frag_pkt, true);
641 
642 	if (net_pkt_set_data(frag_pkt, &frag_access)) {
643 		goto fail;
644 	}
645 
646 	net_pkt_set_ip_hdr_len(frag_pkt, net_pkt_ip_hdr_len(pkt));
647 	net_pkt_set_ipv6_ext_len(frag_pkt,
648 				 net_pkt_ipv6_ext_len(pkt) +
649 				 sizeof(struct net_ipv6_frag_hdr));
650 
651 	/* Finally we copy the payload part of this fragment from
652 	 * the original packet
653 	 */
654 	if (net_pkt_skip(pkt, frag_offset) ||
655 	    net_pkt_copy(frag_pkt, pkt, fit_len)) {
656 		goto fail;
657 	}
658 
659 	net_pkt_cursor_init(frag_pkt);
660 
661 	if (net_ipv6_finalize(frag_pkt, frag_pkt_next_hdr) < 0) {
662 		goto fail;
663 	}
664 
665 	if (final) {
666 		net_pkt_set_context(frag_pkt, net_pkt_context(pkt));
667 	}
668 
669 	/* If everything has been ok so far, we can send the packet. */
670 	ret = net_send_data(frag_pkt);
671 	if (ret < 0) {
672 		goto fail;
673 	}
674 
675 	/* Let this packet to be sent and hopefully it will release
676 	 * the memory that can be utilized for next sent IPv6 fragment.
677 	 */
678 	k_yield();
679 
680 	return 0;
681 
682 fail:
683 	NET_DBG("Cannot send fragment (%d)", ret);
684 	net_pkt_unref(frag_pkt);
685 
686 	return ret;
687 }
688 
net_ipv6_send_fragmented_pkt(struct net_if * iface,struct net_pkt * pkt,uint16_t pkt_len,uint16_t mtu)689 int net_ipv6_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt,
690 				 uint16_t pkt_len, uint16_t mtu)
691 {
692 	uint16_t next_hdr_off;
693 	uint16_t last_hdr_off;
694 	uint16_t frag_offset;
695 	size_t length;
696 	uint8_t next_hdr;
697 	int fit_len;
698 	int ret;
699 
700 	net_pkt_set_ipv6_fragment_id(pkt, sys_rand32_get());
701 
702 	ret = net_ipv6_find_last_ext_hdr(pkt, &next_hdr_off, &last_hdr_off);
703 	if (ret < 0) {
704 		return ret;
705 	}
706 
707 	net_pkt_cursor_init(pkt);
708 
709 	if (net_pkt_skip(pkt, next_hdr_off) ||
710 	    net_pkt_read_u8(pkt, &next_hdr)) {
711 		return -ENOBUFS;
712 	}
713 
714 	/* The Maximum payload can fit into each packet after IPv6 header,
715 	 * Extension headers and Fragmentation header.
716 	 */
717 	fit_len = (int)mtu - NET_IPV6_FRAGH_LEN -
718 		(net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt));
719 
720 	/* The data we want to sent in one fragment must be multiple of 8 */
721 	fit_len = ROUND_DOWN(fit_len, 8);
722 
723 	if (fit_len <= 0) {
724 		/* Must be invalid extension headers length */
725 		NET_DBG("No room for IPv6 payload MTU %d hdrs_len %d",
726 			mtu, NET_IPV6_FRAGH_LEN +
727 			net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt));
728 		return -EINVAL;
729 	}
730 
731 	frag_offset = 0U;
732 
733 	/* Calculate the L4 checksum (if not done already) before the fragmentation. */
734 	if (!net_pkt_is_chksum_done(pkt)) {
735 		net_pkt_cursor_init(pkt);
736 		net_pkt_skip(pkt, last_hdr_off);
737 
738 		switch (next_hdr) {
739 		case IPPROTO_ICMPV6:
740 			ret = net_icmpv6_finalize(pkt, true);
741 			break;
742 		case IPPROTO_TCP:
743 			ret = net_tcp_finalize(pkt, true);
744 			break;
745 		case IPPROTO_UDP:
746 			ret = net_udp_finalize(pkt, true);
747 			break;
748 		default:
749 			ret = 0;
750 			break;
751 		}
752 
753 		if (ret < 0) {
754 			return ret;
755 		}
756 	}
757 
758 	length = net_pkt_get_len(pkt) -
759 		(net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt));
760 	while (length) {
761 		bool final = false;
762 
763 		if (fit_len >= length) {
764 			final = true;
765 			fit_len = length;
766 		}
767 
768 		ret = send_ipv6_fragment(pkt, fit_len, frag_offset,
769 					 next_hdr_off, next_hdr, final);
770 		if (ret < 0) {
771 			return ret;
772 		}
773 
774 		length -= fit_len;
775 		frag_offset += fit_len;
776 	}
777 
778 	return 0;
779 }
780