mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									113c74d83e
								
							
						
					
					
						commit
						34ae6a1aa0
					
				
					 2 changed files with 17 additions and 4 deletions
				
			
		| 
						 | 
					@ -111,11 +111,24 @@ static inline void ipv4_copy_dscp(unsigned int dscp, struct iphdr *inner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ipv6hdr;
 | 
					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)))
 | 
						if (INET_ECN_is_not_ect(ipv6_get_dsfield(iph)))
 | 
				
			||||||
		return 0;
 | 
							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;
 | 
						return 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -142,7 +155,7 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb)
 | 
				
			||||||
	case cpu_to_be16(ETH_P_IPV6):
 | 
						case cpu_to_be16(ETH_P_IPV6):
 | 
				
			||||||
		if (skb_network_header(skb) + sizeof(struct ipv6hdr) <=
 | 
							if (skb_network_header(skb) + sizeof(struct ipv6hdr) <=
 | 
				
			||||||
		    skb_tail_pointer(skb))
 | 
							    skb_tail_pointer(skb))
 | 
				
			||||||
			return IP6_ECN_set_ce(ipv6_hdr(skb));
 | 
								return IP6_ECN_set_ce(skb, ipv6_hdr(skb));
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,7 @@ static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
 | 
				
			||||||
	struct ipv6hdr *inner_iph = ipipv6_hdr(skb);
 | 
						struct ipv6hdr *inner_iph = ipipv6_hdr(skb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos))
 | 
						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.
 | 
					/* Add encapsulation header.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue