forked from mirrors/linux
		
	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
	
	 Eric Dumazet
						Eric Dumazet