Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 46fb51eb authored by Tom Herbert's avatar Tom Herbert Committed by David S. Miller
Browse files

net: Fix save software checksum complete



Geert reported issues regarding checksum complete and UDP.
The logic introduced in commit 7e3cead5
("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: default avatarGeert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: default avatarTom Herbert <therbert@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4b28252c
Loading
Loading
Loading
Loading
+26 −10
Original line number Diff line number Diff line
@@ -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 &&
	if (likely(!sum)) {
		if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
		    !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;

	}
	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);

+3 −0
Original line number Diff line number Diff line
@@ -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