Skip to content

Commit

Permalink
net: Fix save software checksum complete
Browse files Browse the repository at this point in the history
Geert reported issues regarding checksum complete and UDP.
The logic introduced in commit 7e3cead
("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 <[email protected]>
Signed-off-by: Tom Herbert <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Tom Herbert authored and davem330 committed Jun 15, 2014
1 parent 4b28252 commit 46fb51e
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 10 deletions.
36 changes: 26 additions & 10 deletions net/core/datagram.c
Original file line number Diff line number Diff line change
Expand Up @@ -739,22 +739,38 @@ __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
__sum16 sum;

sum = csum_fold(skb_checksum(skb, 0, len, skb->csum));
if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && !sum &&
!skb->csum_complete_sw)
netdev_rx_csum_fault(skb->dev);

/* Save checksum complete for later use */
skb->csum = sum;
skb->ip_summed = CHECKSUM_COMPLETE;
skb->csum_complete_sw = 1;

if (likely(!sum)) {
if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
!skb->csum_complete_sw)
netdev_rx_csum_fault(skb->dev);
}
skb->csum_valid = !sum;
return sum;
}
EXPORT_SYMBOL(__skb_checksum_complete_head);

__sum16 __skb_checksum_complete(struct sk_buff *skb)
{
return __skb_checksum_complete_head(skb, skb->len);
__wsum csum;
__sum16 sum;

csum = skb_checksum(skb, 0, skb->len, 0);

/* skb->csum holds pseudo checksum */
sum = csum_fold(csum_add(skb->csum, csum));
if (likely(!sum)) {
if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
!skb->csum_complete_sw)
netdev_rx_csum_fault(skb->dev);
}

/* Save full packet checksum */
skb->csum = csum;
skb->ip_summed = CHECKSUM_COMPLETE;
skb->csum_complete_sw = 1;
skb->csum_valid = !sum;

return sum;
}
EXPORT_SYMBOL(__skb_checksum_complete);

Expand Down
3 changes: 3 additions & 0 deletions net/core/skbuff.c
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,9 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
new->ooo_okay = old->ooo_okay;
new->no_fcs = old->no_fcs;
new->encapsulation = old->encapsulation;
new->encap_hdr_csum = old->encap_hdr_csum;
new->csum_valid = old->csum_valid;
new->csum_complete_sw = old->csum_complete_sw;
#ifdef CONFIG_XFRM
new->sp = secpath_get(old->sp);
#endif
Expand Down

0 comments on commit 46fb51e

Please sign in to comment.