aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2016-01-15 07:56:56 -0500
committerDavid S. Miller <davem@davemloft.net>2016-01-15 15:07:23 -0500
commit34ae6a1aa0540f0f781dd265366036355fdc8930 (patch)
tree11df908434ac0da470b34a08fd254df1429c4e65
parent113c74d83eef870e43a0d9279044e9d5435f0d07 (diff)
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: Eric Dumazet <edumazet@google.com> Acked-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/inet_ecn.h19
-rw-r--r--net/ipv6/xfrm6_mode_tunnel.c2
2 files changed, 17 insertions, 4 deletions
diff --git a/include/net/inet_ecn.h b/include/net/inet_ecn.h
index 84b20835b736..0dc0a51da38f 100644
--- a/include/net/inet_ecn.h
+++ b/include/net/inet_ecn.h
@@ -111,11 +111,24 @@ static inline void ipv4_copy_dscp(unsigned int dscp, struct iphdr *inner)
111 111
112struct ipv6hdr; 112struct ipv6hdr;
113 113
114static inline int IP6_ECN_set_ce(struct ipv6hdr *iph) 114/* Note:
115 * IP_ECN_set_ce() has to tweak IPV4 checksum when setting CE,
116 * meaning both changes have no effect on skb->csum if/when CHECKSUM_COMPLETE
117 * In IPv6 case, no checksum compensates the change in IPv6 header,
118 * so we have to update skb->csum.
119 */
120static inline int IP6_ECN_set_ce(struct sk_buff *skb, struct ipv6hdr *iph)
115{ 121{
122 __be32 from, to;
123
116 if (INET_ECN_is_not_ect(ipv6_get_dsfield(iph))) 124 if (INET_ECN_is_not_ect(ipv6_get_dsfield(iph)))
117 return 0; 125 return 0;
118 *(__be32*)iph |= htonl(INET_ECN_CE << 20); 126
127 from = *(__be32 *)iph;
128 to = from | htonl(INET_ECN_CE << 20);
129 *(__be32 *)iph = to;
130 if (skb->ip_summed == CHECKSUM_COMPLETE)
131 skb->csum = csum_add(csum_sub(skb->csum, from), to);
119 return 1; 132 return 1;
120} 133}
121 134
@@ -142,7 +155,7 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb)
142 case cpu_to_be16(ETH_P_IPV6): 155 case cpu_to_be16(ETH_P_IPV6):
143 if (skb_network_header(skb) + sizeof(struct ipv6hdr) <= 156 if (skb_network_header(skb) + sizeof(struct ipv6hdr) <=
144 skb_tail_pointer(skb)) 157 skb_tail_pointer(skb))
145 return IP6_ECN_set_ce(ipv6_hdr(skb)); 158 return IP6_ECN_set_ce(skb, ipv6_hdr(skb));
146 break; 159 break;
147 } 160 }
148 161
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
index f7fbdbabe50e..372855eeaf42 100644
--- a/net/ipv6/xfrm6_mode_tunnel.c
+++ b/net/ipv6/xfrm6_mode_tunnel.c
@@ -23,7 +23,7 @@ static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
23 struct ipv6hdr *inner_iph = ipipv6_hdr(skb); 23 struct ipv6hdr *inner_iph = ipipv6_hdr(skb);
24 24
25 if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos)) 25 if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos))
26 IP6_ECN_set_ce(inner_iph); 26 IP6_ECN_set_ce(skb, inner_iph);
27} 27}
28 28
29/* Add encapsulation header. 29/* Add encapsulation header.