mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	tcp6: don't move IP6CB before xfrm6_policy_check()
When xfrm6_policy_check() is used, _decode_session6() is called after some
intermediate functions. This function uses IP6CB(), thus TCP_SKB_CB() must be
prepared after the call of xfrm6_policy_check().
Before this patch, scenarii with IPv6 + TCP + IPsec Transport are broken.
Fixes: 971f10eca1 ("tcp: better TCP_SKB_CB layout to reduce cache line misses")
Reported-by: Huaibin Wang <huaibin.wang@6wind.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									5ad24def21
								
							
						
					
					
						commit
						2dc49d1680
					
				
					 1 changed files with 29 additions and 16 deletions
				
			
		| 
						 | 
					@ -1387,6 +1387,28 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr,
 | 
				
			||||||
 | 
								   const struct tcphdr *th)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* This is tricky: we move IP6CB at its correct location into
 | 
				
			||||||
 | 
						 * TCP_SKB_CB(). It must be done after xfrm6_policy_check(), because
 | 
				
			||||||
 | 
						 * _decode_session6() uses IP6CB().
 | 
				
			||||||
 | 
						 * barrier() makes sure compiler won't play aliasing games.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						memmove(&TCP_SKB_CB(skb)->header.h6, IP6CB(skb),
 | 
				
			||||||
 | 
							sizeof(struct inet6_skb_parm));
 | 
				
			||||||
 | 
						barrier();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TCP_SKB_CB(skb)->seq = ntohl(th->seq);
 | 
				
			||||||
 | 
						TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
 | 
				
			||||||
 | 
									    skb->len - th->doff*4);
 | 
				
			||||||
 | 
						TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
 | 
				
			||||||
 | 
						TCP_SKB_CB(skb)->tcp_flags = tcp_flag_byte(th);
 | 
				
			||||||
 | 
						TCP_SKB_CB(skb)->tcp_tw_isn = 0;
 | 
				
			||||||
 | 
						TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr);
 | 
				
			||||||
 | 
						TCP_SKB_CB(skb)->sacked = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int tcp_v6_rcv(struct sk_buff *skb)
 | 
					static int tcp_v6_rcv(struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const struct tcphdr *th;
 | 
						const struct tcphdr *th;
 | 
				
			||||||
| 
						 | 
					@ -1418,24 +1440,9 @@ static int tcp_v6_rcv(struct sk_buff *skb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	th = tcp_hdr(skb);
 | 
						th = tcp_hdr(skb);
 | 
				
			||||||
	hdr = ipv6_hdr(skb);
 | 
						hdr = ipv6_hdr(skb);
 | 
				
			||||||
	/* This is tricky : We move IPCB at its correct location into TCP_SKB_CB()
 | 
					 | 
				
			||||||
	 * barrier() makes sure compiler wont play fool^Waliasing games.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	memmove(&TCP_SKB_CB(skb)->header.h6, IP6CB(skb),
 | 
					 | 
				
			||||||
		sizeof(struct inet6_skb_parm));
 | 
					 | 
				
			||||||
	barrier();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TCP_SKB_CB(skb)->seq = ntohl(th->seq);
 | 
					 | 
				
			||||||
	TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
 | 
					 | 
				
			||||||
				    skb->len - th->doff*4);
 | 
					 | 
				
			||||||
	TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
 | 
					 | 
				
			||||||
	TCP_SKB_CB(skb)->tcp_flags = tcp_flag_byte(th);
 | 
					 | 
				
			||||||
	TCP_SKB_CB(skb)->tcp_tw_isn = 0;
 | 
					 | 
				
			||||||
	TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr);
 | 
					 | 
				
			||||||
	TCP_SKB_CB(skb)->sacked = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest,
 | 
						sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest,
 | 
				
			||||||
				tcp_v6_iif(skb));
 | 
									inet6_iif(skb));
 | 
				
			||||||
	if (!sk)
 | 
						if (!sk)
 | 
				
			||||||
		goto no_tcp_socket;
 | 
							goto no_tcp_socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1451,6 +1458,8 @@ static int tcp_v6_rcv(struct sk_buff *skb)
 | 
				
			||||||
	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
 | 
						if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
 | 
				
			||||||
		goto discard_and_relse;
 | 
							goto discard_and_relse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tcp_v6_fill_cb(skb, hdr, th);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_TCP_MD5SIG
 | 
					#ifdef CONFIG_TCP_MD5SIG
 | 
				
			||||||
	if (tcp_v6_inbound_md5_hash(sk, skb))
 | 
						if (tcp_v6_inbound_md5_hash(sk, skb))
 | 
				
			||||||
		goto discard_and_relse;
 | 
							goto discard_and_relse;
 | 
				
			||||||
| 
						 | 
					@ -1482,6 +1491,8 @@ static int tcp_v6_rcv(struct sk_buff *skb)
 | 
				
			||||||
	if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
 | 
						if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
 | 
				
			||||||
		goto discard_it;
 | 
							goto discard_it;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tcp_v6_fill_cb(skb, hdr, th);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
 | 
						if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
 | 
				
			||||||
csum_error:
 | 
					csum_error:
 | 
				
			||||||
		TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS);
 | 
							TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS);
 | 
				
			||||||
| 
						 | 
					@ -1505,6 +1516,8 @@ static int tcp_v6_rcv(struct sk_buff *skb)
 | 
				
			||||||
		goto discard_it;
 | 
							goto discard_it;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tcp_v6_fill_cb(skb, hdr, th);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (skb->len < (th->doff<<2)) {
 | 
						if (skb->len < (th->doff<<2)) {
 | 
				
			||||||
		inet_twsk_put(inet_twsk(sk));
 | 
							inet_twsk_put(inet_twsk(sk));
 | 
				
			||||||
		goto bad_packet;
 | 
							goto bad_packet;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue