Commit 58b45f40 authored by Eric Dumazet's avatar Eric Dumazet Committed by Ben Hutchings

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

[ Upstream commit 34ae6a1a ]

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>
[bwh: Backported to 3.2:
 - Adjust context
 - Add skb argument to other callers of IP6_ECN_set_ce()]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 37e9e7ac
......@@ -109,11 +109,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;
}
......@@ -138,7 +151,7 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb)
case cpu_to_be16(ETH_P_IPV6):
if (skb->network_header + sizeof(struct ipv6hdr) <= skb->tail)
return IP6_ECN_set_ce(ipv6_hdr(skb));
return IP6_ECN_set_ce(skb, ipv6_hdr(skb));
break;
}
......
......@@ -535,7 +535,7 @@ static inline void ipgre_ecn_decapsulate(const struct iphdr *iph, struct sk_buff
if (skb->protocol == htons(ETH_P_IP)) {
IP_ECN_set_ce(ip_hdr(skb));
} else if (skb->protocol == htons(ETH_P_IPV6)) {
IP6_ECN_set_ce(ipv6_hdr(skb));
IP6_ECN_set_ce(skb, ipv6_hdr(skb));
}
}
}
......
......@@ -689,7 +689,7 @@ static void ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t,
ipv6_copy_dscp(ipv6_get_dsfield(ipv6h), ipv6_hdr(skb));
if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6h)))
IP6_ECN_set_ce(ipv6_hdr(skb));
IP6_ECN_set_ce(skb, ipv6_hdr(skb));
}
/* called with rcu_read_lock() */
......
......@@ -553,7 +553,7 @@ static int ipip6_err(struct sk_buff *skb, u32 info)
static inline void ipip6_ecn_decapsulate(const struct iphdr *iph, struct sk_buff *skb)
{
if (INET_ECN_is_ce(iph->tos))
IP6_ECN_set_ce(ipv6_hdr(skb));
IP6_ECN_set_ce(skb, ipv6_hdr(skb));
}
static int ipip6_rcv(struct sk_buff *skb)
......
......@@ -24,7 +24,7 @@ static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
struct ipv6hdr *inner_iph = ipipv6_hdr(skb);
if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph)))
IP6_ECN_set_ce(inner_iph);
IP6_ECN_set_ce(skb, inner_iph);
}
/* Add encapsulation header.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment