diff options
author | Tom Herbert <therbert@google.com> | 2014-06-15 02:24:03 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-06-15 04:00:49 -0400 |
commit | 46fb51eb96cafb2c148b7b5119adb5e31a2bf3c4 (patch) | |
tree | 8deefff573270378b056f6f0915068a33578b2ad | |
parent | 4b28252cada3d0521ab59751f4240ecdfb9bba18 (diff) |
net: Fix save software checksum complete
Geert reported issues regarding checksum complete and UDP.
The logic introduced in commit 7e3cead5172927732f51fde
("net: Save software checksum complete") is not correct.
This patch:
1) Restores code in __skb_checksum_complete_header except for setting
CHECKSUM_UNNECESSARY. This function may be calculating checksum on
something less than skb->len.
2) Adds saving checksum to __skb_checksum_complete. The full packet
checksum 0..skb->len is calculated without adding in pseudo header.
This value is saved in skb->csum and then the pseudo header is added
to that to derive the checksum for validation.
3) In both __skb_checksum_complete_header and __skb_checksum_complete,
set skb->csum_valid to whether checksum of zero was computed. This
allows skb_csum_unnecessary to return true without changing to
CHECKSUM_UNNECESSARY which was done previously.
4) Copy new csum related bits in __copy_skb_header.
Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/core/datagram.c | 36 | ||||
-rw-r--r-- | net/core/skbuff.c | 3 |
2 files changed, 29 insertions, 10 deletions
diff --git a/net/core/datagram.c b/net/core/datagram.c index 6b1c04ca1d50..488dd1a825c0 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c | |||
@@ -739,22 +739,38 @@ __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len) | |||
739 | __sum16 sum; | 739 | __sum16 sum; |
740 | 740 | ||
741 | sum = csum_fold(skb_checksum(skb, 0, len, skb->csum)); | 741 | sum = csum_fold(skb_checksum(skb, 0, len, skb->csum)); |
742 | if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && !sum && | 742 | if (likely(!sum)) { |
743 | !skb->csum_complete_sw) | 743 | if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && |
744 | netdev_rx_csum_fault(skb->dev); | 744 | !skb->csum_complete_sw) |
745 | 745 | netdev_rx_csum_fault(skb->dev); | |
746 | /* Save checksum complete for later use */ | 746 | } |
747 | skb->csum = sum; | 747 | skb->csum_valid = !sum; |
748 | skb->ip_summed = CHECKSUM_COMPLETE; | ||
749 | skb->csum_complete_sw = 1; | ||
750 | |||
751 | return sum; | 748 | return sum; |
752 | } | 749 | } |
753 | EXPORT_SYMBOL(__skb_checksum_complete_head); | 750 | EXPORT_SYMBOL(__skb_checksum_complete_head); |
754 | 751 | ||
755 | __sum16 __skb_checksum_complete(struct sk_buff *skb) | 752 | __sum16 __skb_checksum_complete(struct sk_buff *skb) |
756 | { | 753 | { |
757 | return __skb_checksum_complete_head(skb, skb->len); | 754 | __wsum csum; |
755 | __sum16 sum; | ||
756 | |||
757 | csum = skb_checksum(skb, 0, skb->len, 0); | ||
758 | |||
759 | /* skb->csum holds pseudo checksum */ | ||
760 | sum = csum_fold(csum_add(skb->csum, csum)); | ||
761 | if (likely(!sum)) { | ||
762 | if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && | ||
763 | !skb->csum_complete_sw) | ||
764 | netdev_rx_csum_fault(skb->dev); | ||
765 | } | ||
766 | |||
767 | /* Save full packet checksum */ | ||
768 | skb->csum = csum; | ||
769 | skb->ip_summed = CHECKSUM_COMPLETE; | ||
770 | skb->csum_complete_sw = 1; | ||
771 | skb->csum_valid = !sum; | ||
772 | |||
773 | return sum; | ||
758 | } | 774 | } |
759 | EXPORT_SYMBOL(__skb_checksum_complete); | 775 | EXPORT_SYMBOL(__skb_checksum_complete); |
760 | 776 | ||
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index bf92824af3f7..9cd5344fad73 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
@@ -689,6 +689,9 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) | |||
689 | new->ooo_okay = old->ooo_okay; | 689 | new->ooo_okay = old->ooo_okay; |
690 | new->no_fcs = old->no_fcs; | 690 | new->no_fcs = old->no_fcs; |
691 | new->encapsulation = old->encapsulation; | 691 | new->encapsulation = old->encapsulation; |
692 | new->encap_hdr_csum = old->encap_hdr_csum; | ||
693 | new->csum_valid = old->csum_valid; | ||
694 | new->csum_complete_sw = old->csum_complete_sw; | ||
692 | #ifdef CONFIG_XFRM | 695 | #ifdef CONFIG_XFRM |
693 | new->sp = secpath_get(old->sp); | 696 | new->sp = secpath_get(old->sp); |
694 | #endif | 697 | #endif |