mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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…
	
		Reference in a new issue