summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2019-05-29 07:25:31 -0400
committerDavid S. Miller <davem@davemloft.net>2019-05-30 17:18:17 -0400
commitc8b17be0b7a45d707fc202c11d257c25bc3952b8 (patch)
treeebf07ec10ea419e1be811da58d0c09b73ce1d962
parentd48ecb40b5b5a156ae3658dc336bfc29ea502eb6 (diff)
net: ipv4: add skbuff fraglist splitter
This patch adds the skbuff fraglist splitter. This API provides an iterator to transform the fraglist into single skbuff objects, it consists of: * ip_fraglist_init(), that initializes the internal state of the fraglist splitter. * ip_fraglist_prepare(), that restores the IPv4 header on the fragments. * ip_fraglist_next(), that retrieves the fragment from the fraglist and it updates the internal state of the splitter to point to the next fragment skbuff in the fraglist. The ip_fraglist_iter object stores the internal state of the iterator. This code has been extracted from ip_do_fragment(). Symbols are also exported to allow to reuse this iterator from the bridge codepath to build its own refragmentation routine by reusing the existing codebase. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/ip.h23
-rw-r--r--net/ipv4/ip_output.c88
2 files changed, 78 insertions, 33 deletions
diff --git a/include/net/ip.h b/include/net/ip.h
index 2d3cce7c3e8a..be899677504b 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -165,6 +165,29 @@ int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb);
165int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb); 165int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb);
166int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, 166int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
167 int (*output)(struct net *, struct sock *, struct sk_buff *)); 167 int (*output)(struct net *, struct sock *, struct sk_buff *));
168
169struct ip_fraglist_iter {
170 struct sk_buff *frag_list;
171 struct sk_buff *frag;
172 struct iphdr *iph;
173 int offset;
174 unsigned int hlen;
175};
176
177void ip_fraglist_init(struct sk_buff *skb, struct iphdr *iph,
178 unsigned int hlen, struct ip_fraglist_iter *iter);
179void ip_fraglist_prepare(struct sk_buff *skb, struct ip_fraglist_iter *iter);
180
181static inline struct sk_buff *ip_fraglist_next(struct ip_fraglist_iter *iter)
182{
183 struct sk_buff *skb = iter->frag;
184
185 iter->frag = skb->next;
186 skb_mark_not_on_list(skb);
187
188 return skb;
189}
190
168void ip_send_check(struct iphdr *ip); 191void ip_send_check(struct iphdr *ip);
169int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); 192int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
170int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); 193int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index bfd0ca554977..d03eb4ae0dd4 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -561,6 +561,54 @@ static int ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
561 return ip_do_fragment(net, sk, skb, output); 561 return ip_do_fragment(net, sk, skb, output);
562} 562}
563 563
564void ip_fraglist_init(struct sk_buff *skb, struct iphdr *iph,
565 unsigned int hlen, struct ip_fraglist_iter *iter)
566{
567 unsigned int first_len = skb_pagelen(skb);
568
569 iter->frag_list = skb_shinfo(skb)->frag_list;
570 iter->frag = iter->frag_list;
571 skb_frag_list_init(skb);
572
573 iter->offset = 0;
574 iter->iph = iph;
575 iter->hlen = hlen;
576
577 skb->data_len = first_len - skb_headlen(skb);
578 skb->len = first_len;
579 iph->tot_len = htons(first_len);
580 iph->frag_off = htons(IP_MF);
581 ip_send_check(iph);
582}
583EXPORT_SYMBOL(ip_fraglist_init);
584
585void ip_fraglist_prepare(struct sk_buff *skb, struct ip_fraglist_iter *iter)
586{
587 unsigned int hlen = iter->hlen;
588 struct iphdr *iph = iter->iph;
589 struct sk_buff *frag;
590
591 frag = iter->frag;
592 frag->ip_summed = CHECKSUM_NONE;
593 skb_reset_transport_header(frag);
594 __skb_push(frag, hlen);
595 skb_reset_network_header(frag);
596 memcpy(skb_network_header(frag), iph, hlen);
597 iter->iph = ip_hdr(frag);
598 iph = iter->iph;
599 iph->tot_len = htons(frag->len);
600 ip_copy_metadata(frag, skb);
601 if (iter->offset == 0)
602 ip_options_fragment(frag);
603 iter->offset += skb->len - hlen;
604 iph->frag_off = htons(iter->offset >> 3);
605 if (frag->next)
606 iph->frag_off |= htons(IP_MF);
607 /* Ready, complete checksum */
608 ip_send_check(iph);
609}
610EXPORT_SYMBOL(ip_fraglist_prepare);
611
564/* 612/*
565 * This IP datagram is too large to be sent in one piece. Break it up into 613 * This IP datagram is too large to be sent in one piece. Break it up into
566 * smaller pieces (each of size equal to IP header plus 614 * smaller pieces (each of size equal to IP header plus
@@ -578,6 +626,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
578 int offset; 626 int offset;
579 __be16 not_last_frag; 627 __be16 not_last_frag;
580 struct rtable *rt = skb_rtable(skb); 628 struct rtable *rt = skb_rtable(skb);
629 struct ip_fraglist_iter iter;
581 int err = 0; 630 int err = 0;
582 631
583 /* for offloaded checksums cleanup checksum before fragmentation */ 632 /* for offloaded checksums cleanup checksum before fragmentation */
@@ -642,49 +691,22 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
642 } 691 }
643 692
644 /* Everything is OK. Generate! */ 693 /* Everything is OK. Generate! */
645 694 ip_fraglist_init(skb, iph, hlen, &iter);
646 err = 0;
647 offset = 0;
648 frag = skb_shinfo(skb)->frag_list;
649 skb_frag_list_init(skb);
650 skb->data_len = first_len - skb_headlen(skb);
651 skb->len = first_len;
652 iph->tot_len = htons(first_len);
653 iph->frag_off = htons(IP_MF);
654 ip_send_check(iph);
655 695
656 for (;;) { 696 for (;;) {
657 /* Prepare header of the next frame, 697 /* Prepare header of the next frame,
658 * before previous one went down. */ 698 * before previous one went down. */
659 if (frag) { 699 if (iter.frag)
660 frag->ip_summed = CHECKSUM_NONE; 700 ip_fraglist_prepare(skb, &iter);
661 skb_reset_transport_header(frag);
662 __skb_push(frag, hlen);
663 skb_reset_network_header(frag);
664 memcpy(skb_network_header(frag), iph, hlen);
665 iph = ip_hdr(frag);
666 iph->tot_len = htons(frag->len);
667 ip_copy_metadata(frag, skb);
668 if (offset == 0)
669 ip_options_fragment(frag);
670 offset += skb->len - hlen;
671 iph->frag_off = htons(offset>>3);
672 if (frag->next)
673 iph->frag_off |= htons(IP_MF);
674 /* Ready, complete checksum */
675 ip_send_check(iph);
676 }
677 701
678 err = output(net, sk, skb); 702 err = output(net, sk, skb);
679 703
680 if (!err) 704 if (!err)
681 IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES); 705 IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES);
682 if (err || !frag) 706 if (err || !iter.frag)
683 break; 707 break;
684 708
685 skb = frag; 709 skb = ip_fraglist_next(&iter);
686 frag = skb->next;
687 skb_mark_not_on_list(skb);
688 } 710 }
689 711
690 if (err == 0) { 712 if (err == 0) {
@@ -692,7 +714,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
692 return 0; 714 return 0;
693 } 715 }
694 716
695 kfree_skb_list(frag); 717 kfree_skb_list(iter.frag_list);
696 718
697 IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); 719 IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
698 return err; 720 return err;