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,struct in6_addr * src,struct in6_addr * dst)123 static struct net_ipv6_reassembly *reassembly_get(uint32_t id,
124 struct in6_addr *src,
125 struct in6_addr *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(src, &reassembly[i].src) &&
133 net_ipv6_addr_cmp(dst, &reassembly[i].dst)) {
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_ipaddr_copy(&reassembly[avail].src, src);
153 net_ipaddr_copy(&reassembly[avail].dst, 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, (struct in6_addr *)hdr->src,
496 (struct in6_addr *)hdr->dst);
497 if (!reass) {
498 NET_DBG("Cannot get reassembly slot, dropping pkt %p", pkt);
499 goto drop;
500 }
501
502 more = flag & 0x01;
503 net_pkt_set_ipv6_fragment_flags(pkt, flag);
504
505 if (more && net_pkt_get_len(pkt) % 8) {
506 /* Fragment length is not multiple of 8, discard
507 * the packet and send parameter problem error with the
508 * offset of the "Payload Length" field in the IPv6 header.
509 */
510 net_icmpv6_send_error(pkt, NET_ICMPV6_PARAM_PROBLEM,
511 NET_ICMPV6_PARAM_PROB_HEADER, NET_IPV6H_LENGTH_OFFSET);
512 goto drop;
513 }
514
515 /* The fragments might come in wrong order so place them
516 * in reassembly chain in correct order.
517 */
518 for (i = 0, found = false; i < CONFIG_NET_IPV6_FRAGMENT_MAX_PKT; i++) {
519 if (reass->pkt[i]) {
520 if (net_pkt_ipv6_fragment_offset(reass->pkt[i]) <
521 net_pkt_ipv6_fragment_offset(pkt)) {
522 continue;
523 }
524
525 /* Make room for this fragment. If there is no room,
526 * then it will discard the whole reassembly.
527 */
528 if (shift_packets(reass, i)) {
529 break;
530 }
531 }
532
533 NET_DBG("Storing pkt %p to slot %d offset %d",
534 pkt, i, net_pkt_ipv6_fragment_offset(pkt));
535 reass->pkt[i] = pkt;
536 found = true;
537
538 break;
539 }
540
541 if (!found) {
542 /* We could not add this fragment into our saved fragment
543 * list. We must discard the whole packet at this point.
544 */
545 NET_DBG("No slots available for 0x%x", reass->id);
546 net_pkt_unref(pkt);
547 goto drop;
548 }
549
550 ret = fragments_are_ready(reass);
551 if (ret < 0) {
552 NET_DBG("Reassembled IPv6 verify failed, dropping id %u",
553 reass->id);
554
555 /* Let the caller release the already inserted pkt */
556 if (i < CONFIG_NET_IPV6_FRAGMENT_MAX_PKT) {
557 reass->pkt[i] = NULL;
558 }
559
560 net_pkt_unref(pkt);
561 goto drop;
562 } else if (ret == 0) {
563 reassembly_info("Reassembly nth pkt", reass);
564
565 NET_DBG("More fragments to be received");
566 goto accept;
567 }
568
569 reassembly_info("Reassembly last pkt", reass);
570
571 /* The last fragment received, reassemble the packet */
572 reassemble_packet(reass);
573
574 accept:
575 return NET_OK;
576
577 drop:
578 if (reass) {
579 if (reassembly_cancel(reass->id, &reass->src, &reass->dst)) {
580 return NET_OK;
581 }
582 }
583
584 return NET_DROP;
585 }
586
587 #define BUF_ALLOC_TIMEOUT K_MSEC(100)
588
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)589 static int send_ipv6_fragment(struct net_pkt *pkt,
590 uint16_t fit_len,
591 uint16_t frag_offset,
592 uint16_t next_hdr_off,
593 uint8_t next_hdr,
594 bool final)
595 {
596 NET_PKT_DATA_ACCESS_DEFINE(frag_access, struct net_ipv6_frag_hdr);
597 uint8_t frag_pkt_next_hdr = NET_IPV6_NEXTHDR_HBHO;
598 int ret = -ENOBUFS;
599 struct net_ipv6_frag_hdr *frag_hdr;
600 struct net_pkt *frag_pkt;
601
602 frag_pkt = net_pkt_alloc_with_buffer(net_pkt_iface(pkt), fit_len +
603 net_pkt_ipv6_ext_len(pkt) +
604 NET_IPV6_FRAGH_LEN,
605 AF_INET6, 0, BUF_ALLOC_TIMEOUT);
606 if (!frag_pkt) {
607 return -ENOMEM;
608 }
609
610 net_pkt_cursor_init(pkt);
611
612 /* We copy original headers back to the fragment packet
613 * Note that we insert the right next header to point to fragment header
614 */
615 if (net_pkt_copy(frag_pkt, pkt, next_hdr_off) ||
616 net_pkt_write_u8(frag_pkt, NET_IPV6_NEXTHDR_FRAG) ||
617 net_pkt_skip(pkt, 1) ||
618 net_pkt_copy(frag_pkt, pkt, net_pkt_ip_hdr_len(pkt) +
619 net_pkt_ipv6_ext_len(pkt) - next_hdr_off - 1)) {
620 goto fail;
621 }
622
623 if (!net_pkt_ipv6_ext_len(pkt)) {
624 frag_pkt_next_hdr = NET_IPV6_NEXTHDR_FRAG;
625 }
626
627 /* And we append the fragmentation header */
628 frag_hdr = (struct net_ipv6_frag_hdr *)net_pkt_get_data(frag_pkt,
629 &frag_access);
630 if (!frag_hdr) {
631 goto fail;
632 }
633
634 frag_hdr->nexthdr = next_hdr;
635 frag_hdr->reserved = 0U;
636 frag_hdr->id = net_pkt_ipv6_fragment_id(pkt);
637 frag_hdr->offset = htons(((frag_offset / 8U) << 3) | !final);
638
639 net_pkt_set_chksum_done(frag_pkt, true);
640
641 if (net_pkt_set_data(frag_pkt, &frag_access)) {
642 goto fail;
643 }
644
645 net_pkt_set_ip_hdr_len(frag_pkt, net_pkt_ip_hdr_len(pkt));
646 net_pkt_set_ipv6_ext_len(frag_pkt,
647 net_pkt_ipv6_ext_len(pkt) +
648 sizeof(struct net_ipv6_frag_hdr));
649
650 /* Finally we copy the payload part of this fragment from
651 * the original packet
652 */
653 if (net_pkt_skip(pkt, frag_offset) ||
654 net_pkt_copy(frag_pkt, pkt, fit_len)) {
655 goto fail;
656 }
657
658 net_pkt_cursor_init(frag_pkt);
659
660 if (net_ipv6_finalize(frag_pkt, frag_pkt_next_hdr) < 0) {
661 goto fail;
662 }
663
664 if (final) {
665 net_pkt_set_context(frag_pkt, net_pkt_context(pkt));
666 }
667
668 /* If everything has been ok so far, we can send the packet. */
669 ret = net_send_data(frag_pkt);
670 if (ret < 0) {
671 goto fail;
672 }
673
674 /* Let this packet to be sent and hopefully it will release
675 * the memory that can be utilized for next sent IPv6 fragment.
676 */
677 k_yield();
678
679 return 0;
680
681 fail:
682 NET_DBG("Cannot send fragment (%d)", ret);
683 net_pkt_unref(frag_pkt);
684
685 return ret;
686 }
687
net_ipv6_send_fragmented_pkt(struct net_if * iface,struct net_pkt * pkt,uint16_t pkt_len,uint16_t mtu)688 int net_ipv6_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt,
689 uint16_t pkt_len, uint16_t mtu)
690 {
691 uint16_t next_hdr_off;
692 uint16_t last_hdr_off;
693 uint16_t frag_offset;
694 size_t length;
695 uint8_t next_hdr;
696 int fit_len;
697 int ret;
698
699 net_pkt_set_ipv6_fragment_id(pkt, sys_rand32_get());
700
701 ret = net_ipv6_find_last_ext_hdr(pkt, &next_hdr_off, &last_hdr_off);
702 if (ret < 0) {
703 return ret;
704 }
705
706 net_pkt_cursor_init(pkt);
707
708 if (net_pkt_skip(pkt, next_hdr_off) ||
709 net_pkt_read_u8(pkt, &next_hdr)) {
710 return -ENOBUFS;
711 }
712
713 /* The Maximum payload can fit into each packet after IPv6 header,
714 * Extension headers and Fragmentation header.
715 */
716 fit_len = (int)mtu - NET_IPV6_FRAGH_LEN -
717 (net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt));
718
719 /* The data we want to sent in one fragment must be multiple of 8 */
720 fit_len = ROUND_DOWN(fit_len, 8);
721
722 if (fit_len <= 0) {
723 /* Must be invalid extension headers length */
724 NET_DBG("No room for IPv6 payload MTU %d hdrs_len %d",
725 mtu, NET_IPV6_FRAGH_LEN +
726 net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt));
727 return -EINVAL;
728 }
729
730 frag_offset = 0U;
731
732 /* Calculate the L4 checksum (if not done already) before the fragmentation. */
733 if (!net_pkt_is_chksum_done(pkt)) {
734 net_pkt_cursor_init(pkt);
735 net_pkt_skip(pkt, last_hdr_off);
736
737 switch (next_hdr) {
738 case IPPROTO_ICMPV6:
739 ret = net_icmpv6_finalize(pkt, true);
740 break;
741 case IPPROTO_TCP:
742 ret = net_tcp_finalize(pkt, true);
743 break;
744 case IPPROTO_UDP:
745 ret = net_udp_finalize(pkt, true);
746 break;
747 default:
748 ret = 0;
749 break;
750 }
751
752 if (ret < 0) {
753 return ret;
754 }
755 }
756
757 length = net_pkt_get_len(pkt) -
758 (net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt));
759 while (length) {
760 bool final = false;
761
762 if (fit_len >= length) {
763 final = true;
764 fit_len = length;
765 }
766
767 ret = send_ipv6_fragment(pkt, fit_len, frag_offset,
768 next_hdr_off, next_hdr, final);
769 if (ret < 0) {
770 return ret;
771 }
772
773 length -= fit_len;
774 frag_offset += fit_len;
775 }
776
777 return 0;
778 }
779