@@ -592,14 +592,80 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
592
592
skb_copy_secmark (to , from );
593
593
}
594
594
595
+ int ip6_fraglist_init (struct sk_buff * skb , unsigned int hlen , u8 * prevhdr ,
596
+ u8 nexthdr , __be32 frag_id ,
597
+ struct ip6_fraglist_iter * iter )
598
+ {
599
+ unsigned int first_len ;
600
+ struct frag_hdr * fh ;
601
+
602
+ /* BUILD HEADER */
603
+ * prevhdr = NEXTHDR_FRAGMENT ;
604
+ iter -> tmp_hdr = kmemdup (skb_network_header (skb ), hlen , GFP_ATOMIC );
605
+ if (!iter -> tmp_hdr )
606
+ return - ENOMEM ;
607
+
608
+ iter -> frag_list = skb_shinfo (skb )-> frag_list ;
609
+ iter -> frag = iter -> frag_list ;
610
+ skb_frag_list_init (skb );
611
+
612
+ iter -> offset = 0 ;
613
+ iter -> hlen = hlen ;
614
+ iter -> frag_id = frag_id ;
615
+ iter -> nexthdr = nexthdr ;
616
+
617
+ __skb_pull (skb , hlen );
618
+ fh = __skb_push (skb , sizeof (struct frag_hdr ));
619
+ __skb_push (skb , hlen );
620
+ skb_reset_network_header (skb );
621
+ memcpy (skb_network_header (skb ), iter -> tmp_hdr , hlen );
622
+
623
+ fh -> nexthdr = nexthdr ;
624
+ fh -> reserved = 0 ;
625
+ fh -> frag_off = htons (IP6_MF );
626
+ fh -> identification = frag_id ;
627
+
628
+ first_len = skb_pagelen (skb );
629
+ skb -> data_len = first_len - skb_headlen (skb );
630
+ skb -> len = first_len ;
631
+ ipv6_hdr (skb )-> payload_len = htons (first_len - sizeof (struct ipv6hdr ));
632
+
633
+ return 0 ;
634
+ }
635
+ EXPORT_SYMBOL (ip6_fraglist_init );
636
+
637
+ void ip6_fraglist_prepare (struct sk_buff * skb ,
638
+ struct ip6_fraglist_iter * iter )
639
+ {
640
+ struct sk_buff * frag = iter -> frag ;
641
+ unsigned int hlen = iter -> hlen ;
642
+ struct frag_hdr * fh ;
643
+
644
+ frag -> ip_summed = CHECKSUM_NONE ;
645
+ skb_reset_transport_header (frag );
646
+ fh = __skb_push (frag , sizeof (struct frag_hdr ));
647
+ __skb_push (frag , hlen );
648
+ skb_reset_network_header (frag );
649
+ memcpy (skb_network_header (frag ), iter -> tmp_hdr , hlen );
650
+ iter -> offset += skb -> len - hlen - sizeof (struct frag_hdr );
651
+ fh -> nexthdr = iter -> nexthdr ;
652
+ fh -> reserved = 0 ;
653
+ fh -> frag_off = htons (iter -> offset );
654
+ if (frag -> next )
655
+ fh -> frag_off |= htons (IP6_MF );
656
+ fh -> identification = iter -> frag_id ;
657
+ ipv6_hdr (frag )-> payload_len = htons (frag -> len - sizeof (struct ipv6hdr ));
658
+ ip6_copy_metadata (frag , skb );
659
+ }
660
+ EXPORT_SYMBOL (ip6_fraglist_prepare );
661
+
595
662
int ip6_fragment (struct net * net , struct sock * sk , struct sk_buff * skb ,
596
663
int (* output )(struct net * , struct sock * , struct sk_buff * ))
597
664
{
598
665
struct sk_buff * frag ;
599
666
struct rt6_info * rt = (struct rt6_info * )skb_dst (skb );
600
667
struct ipv6_pinfo * np = skb -> sk && !dev_recursion_level () ?
601
668
inet6_sk (skb -> sk ) : NULL ;
602
- struct ipv6hdr * tmp_hdr ;
603
669
struct frag_hdr * fh ;
604
670
unsigned int mtu , hlen , left , len , nexthdr_offset ;
605
671
int hroom , troom ;
@@ -651,6 +717,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
651
717
hroom = LL_RESERVED_SPACE (rt -> dst .dev );
652
718
if (skb_has_frag_list (skb )) {
653
719
unsigned int first_len = skb_pagelen (skb );
720
+ struct ip6_fraglist_iter iter ;
654
721
struct sk_buff * frag2 ;
655
722
656
723
if (first_len - hlen > mtu ||
@@ -678,82 +745,37 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
678
745
skb -> truesize -= frag -> truesize ;
679
746
}
680
747
681
- err = 0 ;
682
- offset = 0 ;
683
- /* BUILD HEADER */
684
-
685
- * prevhdr = NEXTHDR_FRAGMENT ;
686
- tmp_hdr = kmemdup (skb_network_header (skb ), hlen , GFP_ATOMIC );
687
- if (!tmp_hdr ) {
688
- err = - ENOMEM ;
748
+ err = ip6_fraglist_init (skb , hlen , prevhdr , nexthdr , frag_id ,
749
+ & iter );
750
+ if (err < 0 )
689
751
goto fail ;
690
- }
691
- frag = skb_shinfo (skb )-> frag_list ;
692
- skb_frag_list_init (skb );
693
-
694
- __skb_pull (skb , hlen );
695
- fh = __skb_push (skb , sizeof (struct frag_hdr ));
696
- __skb_push (skb , hlen );
697
- skb_reset_network_header (skb );
698
- memcpy (skb_network_header (skb ), tmp_hdr , hlen );
699
-
700
- fh -> nexthdr = nexthdr ;
701
- fh -> reserved = 0 ;
702
- fh -> frag_off = htons (IP6_MF );
703
- fh -> identification = frag_id ;
704
-
705
- first_len = skb_pagelen (skb );
706
- skb -> data_len = first_len - skb_headlen (skb );
707
- skb -> len = first_len ;
708
- ipv6_hdr (skb )-> payload_len = htons (first_len -
709
- sizeof (struct ipv6hdr ));
710
752
711
753
for (;;) {
712
754
/* Prepare header of the next frame,
713
755
* before previous one went down. */
714
- if (frag ) {
715
- frag -> ip_summed = CHECKSUM_NONE ;
716
- skb_reset_transport_header (frag );
717
- fh = __skb_push (frag , sizeof (struct frag_hdr ));
718
- __skb_push (frag , hlen );
719
- skb_reset_network_header (frag );
720
- memcpy (skb_network_header (frag ), tmp_hdr ,
721
- hlen );
722
- offset += skb -> len - hlen - sizeof (struct frag_hdr );
723
- fh -> nexthdr = nexthdr ;
724
- fh -> reserved = 0 ;
725
- fh -> frag_off = htons (offset );
726
- if (frag -> next )
727
- fh -> frag_off |= htons (IP6_MF );
728
- fh -> identification = frag_id ;
729
- ipv6_hdr (frag )-> payload_len =
730
- htons (frag -> len -
731
- sizeof (struct ipv6hdr ));
732
- ip6_copy_metadata (frag , skb );
733
- }
756
+ if (iter .frag )
757
+ ip6_fraglist_prepare (skb , & iter );
734
758
735
759
err = output (net , sk , skb );
736
760
if (!err )
737
761
IP6_INC_STATS (net , ip6_dst_idev (& rt -> dst ),
738
762
IPSTATS_MIB_FRAGCREATES );
739
763
740
- if (err || !frag )
764
+ if (err || !iter . frag )
741
765
break ;
742
766
743
- skb = frag ;
744
- frag = skb -> next ;
745
- skb_mark_not_on_list (skb );
767
+ skb = ip6_fraglist_next (& iter );
746
768
}
747
769
748
- kfree (tmp_hdr );
770
+ kfree (iter . tmp_hdr );
749
771
750
772
if (err == 0 ) {
751
773
IP6_INC_STATS (net , ip6_dst_idev (& rt -> dst ),
752
774
IPSTATS_MIB_FRAGOKS );
753
775
return 0 ;
754
776
}
755
777
756
- kfree_skb_list (frag );
778
+ kfree_skb_list (iter . frag_list );
757
779
758
780
IP6_INC_STATS (net , ip6_dst_idev (& rt -> dst ),
759
781
IPSTATS_MIB_FRAGFAILS );
0 commit comments