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

Commit 34ae6a1a authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

ipv6: update skb->csum when CE mark is propagated



When a tunnel decapsulates the outer header, it has to comply
with RFC 6080 and eventually propagate CE mark into inner header.

It turns out IP6_ECN_set_ce() does not correctly update skb->csum
for CHECKSUM_COMPLETE packets, triggering infamous "hw csum failure"
messages and stack traces.

Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Acked-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 113c74d8
Loading
Loading
Loading
Loading
+16 −3
Original line number Diff line number Diff line
@@ -111,11 +111,24 @@ static inline void ipv4_copy_dscp(unsigned int dscp, struct iphdr *inner)

struct ipv6hdr;

static inline int IP6_ECN_set_ce(struct ipv6hdr *iph)
/* Note:
 * IP_ECN_set_ce() has to tweak IPV4 checksum when setting CE,
 * meaning both changes have no effect on skb->csum if/when CHECKSUM_COMPLETE
 * In IPv6 case, no checksum compensates the change in IPv6 header,
 * so we have to update skb->csum.
 */
static inline int IP6_ECN_set_ce(struct sk_buff *skb, struct ipv6hdr *iph)
{
	__be32 from, to;

	if (INET_ECN_is_not_ect(ipv6_get_dsfield(iph)))
		return 0;
	*(__be32*)iph |= htonl(INET_ECN_CE << 20);

	from = *(__be32 *)iph;
	to = from | htonl(INET_ECN_CE << 20);
	*(__be32 *)iph = to;
	if (skb->ip_summed == CHECKSUM_COMPLETE)
		skb->csum = csum_add(csum_sub(skb->csum, from), to);
	return 1;
}

@@ -142,7 +155,7 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb)
	case cpu_to_be16(ETH_P_IPV6):
		if (skb_network_header(skb) + sizeof(struct ipv6hdr) <=
		    skb_tail_pointer(skb))
			return IP6_ECN_set_ce(ipv6_hdr(skb));
			return IP6_ECN_set_ce(skb, ipv6_hdr(skb));
		break;
	}

+1 −1
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
	struct ipv6hdr *inner_iph = ipipv6_hdr(skb);

	if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos))
		IP6_ECN_set_ce(inner_iph);
		IP6_ECN_set_ce(skb, inner_iph);
}

/* Add encapsulation header.