net: ipv6: fix TCP early demux
IPv6 needs a cookie in dst_check() call. We need to add rx_dst_cookie and provide a family independent sk_rx_dst_set(sk, skb) method to properly support IPv6 TCP early demux. Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
					parent
					
						
							
								b5497eeb37
							
						
					
				
			
			
				commit
				
					
						5d299f3d3c
					
				
			
		
					 7 changed files with 41 additions and 16 deletions
				
			
		|  | @ -369,6 +369,7 @@ struct ipv6_pinfo { | ||||||
| 	__u8			rcv_tclass; | 	__u8			rcv_tclass; | ||||||
| 
 | 
 | ||||||
| 	__u32			dst_cookie; | 	__u32			dst_cookie; | ||||||
|  | 	__u32			rx_dst_cookie; | ||||||
| 
 | 
 | ||||||
| 	struct ipv6_mc_socklist	__rcu *ipv6_mc_list; | 	struct ipv6_mc_socklist	__rcu *ipv6_mc_list; | ||||||
| 	struct ipv6_ac_socklist	*ipv6_ac_list; | 	struct ipv6_ac_socklist	*ipv6_ac_list; | ||||||
|  |  | ||||||
|  | @ -39,6 +39,7 @@ struct inet_connection_sock_af_ops { | ||||||
| 	int	    (*queue_xmit)(struct sk_buff *skb, struct flowi *fl); | 	int	    (*queue_xmit)(struct sk_buff *skb, struct flowi *fl); | ||||||
| 	void	    (*send_check)(struct sock *sk, struct sk_buff *skb); | 	void	    (*send_check)(struct sock *sk, struct sk_buff *skb); | ||||||
| 	int	    (*rebuild_header)(struct sock *sk); | 	int	    (*rebuild_header)(struct sock *sk); | ||||||
|  | 	void	    (*sk_rx_dst_set)(struct sock *sk, const struct sk_buff *skb); | ||||||
| 	int	    (*conn_request)(struct sock *sk, struct sk_buff *skb); | 	int	    (*conn_request)(struct sock *sk, struct sk_buff *skb); | ||||||
| 	struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb, | 	struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb, | ||||||
| 				      struct request_sock *req, | 				      struct request_sock *req, | ||||||
|  |  | ||||||
|  | @ -249,13 +249,4 @@ static inline __u8 inet_sk_flowi_flags(const struct sock *sk) | ||||||
| 	return flags; | 	return flags; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) |  | ||||||
| { |  | ||||||
| 	struct dst_entry *dst = skb_dst(skb); |  | ||||||
| 
 |  | ||||||
| 	dst_hold(dst); |  | ||||||
| 	sk->sk_rx_dst = dst; |  | ||||||
| 	inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #endif	/* _INET_SOCK_H */ | #endif	/* _INET_SOCK_H */ | ||||||
|  |  | ||||||
|  | @ -5392,6 +5392,8 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, | ||||||
| { | { | ||||||
| 	struct tcp_sock *tp = tcp_sk(sk); | 	struct tcp_sock *tp = tcp_sk(sk); | ||||||
| 
 | 
 | ||||||
|  | 	if (unlikely(sk->sk_rx_dst == NULL)) | ||||||
|  | 		inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb); | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 *	Header prediction. | 	 *	Header prediction. | ||||||
| 	 *	The code loosely follows the one in the famous | 	 *	The code loosely follows the one in the famous | ||||||
|  | @ -5605,7 +5607,7 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) | ||||||
| 	tcp_set_state(sk, TCP_ESTABLISHED); | 	tcp_set_state(sk, TCP_ESTABLISHED); | ||||||
| 
 | 
 | ||||||
| 	if (skb != NULL) { | 	if (skb != NULL) { | ||||||
| 		inet_sk_rx_dst_set(sk, skb); | 		icsk->icsk_af_ops->sk_rx_dst_set(sk, skb); | ||||||
| 		security_inet_conn_established(sk, skb); | 		security_inet_conn_established(sk, skb); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1627,9 +1627,6 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) | ||||||
| 				sk->sk_rx_dst = NULL; | 				sk->sk_rx_dst = NULL; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if (unlikely(sk->sk_rx_dst == NULL)) |  | ||||||
| 			inet_sk_rx_dst_set(sk, skb); |  | ||||||
| 
 |  | ||||||
| 		if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) { | 		if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) { | ||||||
| 			rsk = sk; | 			rsk = sk; | ||||||
| 			goto reset; | 			goto reset; | ||||||
|  | @ -1872,10 +1869,20 @@ static struct timewait_sock_ops tcp_timewait_sock_ops = { | ||||||
| 	.twsk_destructor= tcp_twsk_destructor, | 	.twsk_destructor= tcp_twsk_destructor, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct dst_entry *dst = skb_dst(skb); | ||||||
|  | 
 | ||||||
|  | 	dst_hold(dst); | ||||||
|  | 	sk->sk_rx_dst = dst; | ||||||
|  | 	inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const struct inet_connection_sock_af_ops ipv4_specific = { | const struct inet_connection_sock_af_ops ipv4_specific = { | ||||||
| 	.queue_xmit	   = ip_queue_xmit, | 	.queue_xmit	   = ip_queue_xmit, | ||||||
| 	.send_check	   = tcp_v4_send_check, | 	.send_check	   = tcp_v4_send_check, | ||||||
| 	.rebuild_header	   = inet_sk_rebuild_header, | 	.rebuild_header	   = inet_sk_rebuild_header, | ||||||
|  | 	.sk_rx_dst_set	   = inet_sk_rx_dst_set, | ||||||
| 	.conn_request	   = tcp_v4_conn_request, | 	.conn_request	   = tcp_v4_conn_request, | ||||||
| 	.syn_recv_sock	   = tcp_v4_syn_recv_sock, | 	.syn_recv_sock	   = tcp_v4_syn_recv_sock, | ||||||
| 	.net_header_len	   = sizeof(struct iphdr), | 	.net_header_len	   = sizeof(struct iphdr), | ||||||
|  |  | ||||||
|  | @ -387,7 +387,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, | ||||||
| 		struct tcp_sock *oldtp = tcp_sk(sk); | 		struct tcp_sock *oldtp = tcp_sk(sk); | ||||||
| 		struct tcp_cookie_values *oldcvp = oldtp->cookie_values; | 		struct tcp_cookie_values *oldcvp = oldtp->cookie_values; | ||||||
| 
 | 
 | ||||||
| 		inet_sk_rx_dst_set(newsk, skb); | 		newicsk->icsk_af_ops->sk_rx_dst_set(newsk, skb); | ||||||
| 
 | 
 | ||||||
| 		/* TCP Cookie Transactions require space for the cookie pair,
 | 		/* TCP Cookie Transactions require space for the cookie pair,
 | ||||||
| 		 * as it differs for each connection.  There is no need to | 		 * as it differs for each connection.  There is no need to | ||||||
|  |  | ||||||
|  | @ -1447,7 +1447,17 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) | ||||||
| 		opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC)); | 		opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC)); | ||||||
| 
 | 
 | ||||||
| 	if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ | 	if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ | ||||||
|  | 		struct dst_entry *dst = sk->sk_rx_dst; | ||||||
|  | 
 | ||||||
| 		sock_rps_save_rxhash(sk, skb); | 		sock_rps_save_rxhash(sk, skb); | ||||||
|  | 		if (dst) { | ||||||
|  | 			if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || | ||||||
|  | 			    dst->ops->check(dst, np->rx_dst_cookie) == NULL) { | ||||||
|  | 				dst_release(dst); | ||||||
|  | 				sk->sk_rx_dst = NULL; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) | 		if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) | ||||||
| 			goto reset; | 			goto reset; | ||||||
| 		if (opt_skb) | 		if (opt_skb) | ||||||
|  | @ -1705,9 +1715,9 @@ static void tcp_v6_early_demux(struct sk_buff *skb) | ||||||
| 			struct dst_entry *dst = sk->sk_rx_dst; | 			struct dst_entry *dst = sk->sk_rx_dst; | ||||||
| 			struct inet_sock *icsk = inet_sk(sk); | 			struct inet_sock *icsk = inet_sk(sk); | ||||||
| 			if (dst) | 			if (dst) | ||||||
| 				dst = dst_check(dst, 0); | 				dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie); | ||||||
| 			if (dst && | 			if (dst && | ||||||
| 			    icsk->rx_dst_ifindex == inet6_iif(skb)) | 			    icsk->rx_dst_ifindex == skb->skb_iif) | ||||||
| 				skb_dst_set_noref(skb, dst); | 				skb_dst_set_noref(skb, dst); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -1719,10 +1729,23 @@ static struct timewait_sock_ops tcp6_timewait_sock_ops = { | ||||||
| 	.twsk_destructor= tcp_twsk_destructor, | 	.twsk_destructor= tcp_twsk_destructor, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct dst_entry *dst = skb_dst(skb); | ||||||
|  | 	const struct rt6_info *rt = (const struct rt6_info *)dst; | ||||||
|  | 
 | ||||||
|  | 	dst_hold(dst); | ||||||
|  | 	sk->sk_rx_dst = dst; | ||||||
|  | 	inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; | ||||||
|  | 	if (rt->rt6i_node) | ||||||
|  | 		inet6_sk(sk)->rx_dst_cookie = rt->rt6i_node->fn_sernum; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static const struct inet_connection_sock_af_ops ipv6_specific = { | static const struct inet_connection_sock_af_ops ipv6_specific = { | ||||||
| 	.queue_xmit	   = inet6_csk_xmit, | 	.queue_xmit	   = inet6_csk_xmit, | ||||||
| 	.send_check	   = tcp_v6_send_check, | 	.send_check	   = tcp_v6_send_check, | ||||||
| 	.rebuild_header	   = inet6_sk_rebuild_header, | 	.rebuild_header	   = inet6_sk_rebuild_header, | ||||||
|  | 	.sk_rx_dst_set	   = inet6_sk_rx_dst_set, | ||||||
| 	.conn_request	   = tcp_v6_conn_request, | 	.conn_request	   = tcp_v6_conn_request, | ||||||
| 	.syn_recv_sock	   = tcp_v6_syn_recv_sock, | 	.syn_recv_sock	   = tcp_v6_syn_recv_sock, | ||||||
| 	.net_header_len	   = sizeof(struct ipv6hdr), | 	.net_header_len	   = sizeof(struct ipv6hdr), | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Eric Dumazet
				Eric Dumazet