diff options
Diffstat (limited to 'net/ipv6/tcp_ipv6.c')
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 88 | 
1 files changed, 52 insertions, 36 deletions
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index b9f1fee9a886..60a5295a7de6 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -101,12 +101,12 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)  	}  } -static __u32 tcp_v6_init_sequence(const struct sk_buff *skb) +static u32 tcp_v6_init_sequence(const struct sk_buff *skb, u32 *tsoff)  {  	return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,  					    ipv6_hdr(skb)->saddr.s6_addr32,  					    tcp_hdr(skb)->dest, -					    tcp_hdr(skb)->source); +					    tcp_hdr(skb)->source, tsoff);  }  static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, @@ -122,7 +122,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  	struct flowi6 fl6;  	struct dst_entry *dst;  	int addr_type; +	u32 seq;  	int err; +	struct inet_timewait_death_row *tcp_death_row = &sock_net(sk)->ipv4.tcp_death_row;  	if (addr_len < SIN6_LEN_RFC2133)  		return -EINVAL; @@ -148,8 +150,13 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  	 *	connect() to INADDR_ANY means loopback (BSD'ism).  	 */ -	if (ipv6_addr_any(&usin->sin6_addr)) -		usin->sin6_addr.s6_addr[15] = 0x1; +	if (ipv6_addr_any(&usin->sin6_addr)) { +		if (ipv6_addr_v4mapped(&sk->sk_v6_rcv_saddr)) +			ipv6_addr_set_v4mapped(htonl(INADDR_LOOPBACK), +					       &usin->sin6_addr); +		else +			usin->sin6_addr = in6addr_loopback; +	}  	addr_type = ipv6_addr_type(&usin->sin6_addr); @@ -188,7 +195,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  	 *	TCP over IPv4  	 */ -	if (addr_type == IPV6_ADDR_MAPPED) { +	if (addr_type & IPV6_ADDR_MAPPED) {  		u32 exthdrlen = icsk->icsk_ext_hdr_len;  		struct sockaddr_in sin; @@ -233,6 +240,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  	fl6.flowi6_mark = sk->sk_mark;  	fl6.fl6_dport = usin->sin6_port;  	fl6.fl6_sport = inet->inet_sport; +	fl6.flowi6_uid = sk->sk_uid;  	opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk));  	final_p = fl6_update_dst(&fl6, opt, &final); @@ -257,7 +265,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  	sk->sk_gso_type = SKB_GSO_TCPV6;  	ip6_dst_store(sk, dst, NULL, NULL); -	if (tcp_death_row.sysctl_tw_recycle && +	if (tcp_death_row->sysctl_tw_recycle &&  	    !tp->rx_opt.ts_recent_stamp &&  	    ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr))  		tcp_fetch_timewait_stamp(sk, dst); @@ -272,17 +280,26 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  	inet->inet_dport = usin->sin6_port;  	tcp_set_state(sk, TCP_SYN_SENT); -	err = inet6_hash_connect(&tcp_death_row, sk); +	err = inet6_hash_connect(tcp_death_row, sk);  	if (err)  		goto late_failure;  	sk_set_txhash(sk); -	if (!tp->write_seq && likely(!tp->repair)) -		tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32, -							     sk->sk_v6_daddr.s6_addr32, -							     inet->inet_sport, -							     inet->inet_dport); +	if (likely(!tp->repair)) { +		seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32, +						   sk->sk_v6_daddr.s6_addr32, +						   inet->inet_sport, +						   inet->inet_dport, +						   &tp->tsoffset); +		if (!tp->write_seq) +			tp->write_seq = seq; +	} + +	if (tcp_fastopen_defer_connect(sk, &err)) +		return err; +	if (err) +		goto late_failure;  	err = tcp_connect(sk);  	if (err) @@ -292,7 +309,6 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  late_failure:  	tcp_set_state(sk, TCP_CLOSE); -	__sk_dst_reset(sk);  failure:  	inet->inet_dport = 0;  	sk->sk_route_caps = 0; @@ -397,7 +413,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,  		if (!sock_owned_by_user(sk))  			tcp_v6_mtu_reduced(sk);  		else if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, -					   &tp->tsq_flags)) +					   &sk->sk_tsq_flags))  			sock_hold(sk);  		goto out;  	} @@ -467,7 +483,7 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst,  		opt = ireq->ipv6_opt;  		if (!opt)  			opt = rcu_dereference(np->opt); -		err = ip6_xmit(sk, skb, fl6, opt, np->tclass); +		err = ip6_xmit(sk, skb, fl6, sk->sk_mark, opt, np->tclass);  		rcu_read_unlock();  		err = net_xmit_eval(err);  	} @@ -828,6 +844,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32  	fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark);  	fl6.fl6_dport = t1->dest;  	fl6.fl6_sport = t1->source; +	fl6.flowi6_uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL);  	security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));  	/* Pass a socket to ip6_dst_lookup either it is for RST @@ -837,7 +854,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32  	dst = ip6_dst_lookup_flow(ctl_sk, &fl6, NULL);  	if (!IS_ERR(dst)) {  		skb_dst_set(buff, dst); -		ip6_xmit(ctl_sk, buff, &fl6, NULL, tclass); +		ip6_xmit(ctl_sk, buff, &fl6, fl6.flowi6_mark, NULL, tclass);  		TCP_INC_STATS(net, TCP_MIB_OUTSEGS);  		if (rst)  			TCP_INC_STATS(net, TCP_MIB_OUTRSTS); @@ -954,7 +971,8 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,  			tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt,  			tcp_rsk(req)->rcv_nxt,  			req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale, -			tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if, +			tcp_time_stamp + tcp_rsk(req)->ts_off, +			req->ts_recent, sk->sk_bound_dev_if,  			tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr),  			0, 0);  } @@ -987,6 +1005,16 @@ drop:  	return 0; /* don't send reset */  } +static void tcp_v6_restore_cb(struct sk_buff *skb) +{ +	/* We need to move header back to the beginning if xfrm6_policy_check() +	 * and tcp_v6_fill_cb() are going to be called again. +	 * ip6_datagram_recv_specific_ctl() also expects IP6CB to be there. +	 */ +	memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6, +		sizeof(struct inet6_skb_parm)); +} +  static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,  					 struct request_sock *req,  					 struct dst_entry *dst, @@ -1138,10 +1166,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *  	tcp_ca_openreq_child(newsk, dst);  	tcp_sync_mss(newsk, dst_mtu(dst)); -	newtp->advmss = dst_metric_advmss(dst); -	if (tcp_sk(sk)->rx_opt.user_mss && -	    tcp_sk(sk)->rx_opt.user_mss < newtp->advmss) -		newtp->advmss = tcp_sk(sk)->rx_opt.user_mss; +	newtp->advmss = tcp_mss_clamp(tcp_sk(sk), dst_metric_advmss(dst));  	tcp_initialize_rcv_mss(newsk); @@ -1178,8 +1203,10 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *  						      sk_gfp_mask(sk, GFP_ATOMIC));  			consume_skb(ireq->pktopts);  			ireq->pktopts = NULL; -			if (newnp->pktoptions) +			if (newnp->pktoptions) { +				tcp_v6_restore_cb(newnp->pktoptions);  				skb_set_owner_r(newnp->pktoptions, newsk); +			}  		}  	} @@ -1194,16 +1221,6 @@ out:  	return NULL;  } -static void tcp_v6_restore_cb(struct sk_buff *skb) -{ -	/* We need to move header back to the beginning if xfrm6_policy_check() -	 * and tcp_v6_fill_cb() are going to be called again. -	 * ip6_datagram_recv_specific_ctl() also expects IP6CB to be there. -	 */ -	memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6, -		sizeof(struct inet6_skb_parm)); -} -  /* The socket must have it's spinlock held when we get   * here, unless it is a TCP_LISTEN socket.   * @@ -1616,7 +1633,6 @@ static const struct inet_connection_sock_af_ops ipv6_specific = {  	.getsockopt	   = ipv6_getsockopt,  	.addr2sockaddr	   = inet6_csk_addr2sockaddr,  	.sockaddr_len	   = sizeof(struct sockaddr_in6), -	.bind_conflict	   = inet6_csk_bind_conflict,  #ifdef CONFIG_COMPAT  	.compat_setsockopt = compat_ipv6_setsockopt,  	.compat_getsockopt = compat_ipv6_getsockopt, @@ -1647,7 +1663,6 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = {  	.getsockopt	   = ipv6_getsockopt,  	.addr2sockaddr	   = inet6_csk_addr2sockaddr,  	.sockaddr_len	   = sizeof(struct sockaddr_in6), -	.bind_conflict	   = inet6_csk_bind_conflict,  #ifdef CONFIG_COMPAT  	.compat_setsockopt = compat_ipv6_setsockopt,  	.compat_getsockopt = compat_ipv6_getsockopt, @@ -1740,7 +1755,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)  	srcp  = ntohs(inet->inet_sport);  	if (icsk->icsk_pending == ICSK_TIME_RETRANS || -	    icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || +	    icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||  	    icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {  		timer_active	= 1;  		timer_expires	= icsk->icsk_timeout; @@ -1884,6 +1899,7 @@ struct proto tcpv6_prot = {  	.shutdown		= tcp_shutdown,  	.setsockopt		= tcp_setsockopt,  	.getsockopt		= tcp_getsockopt, +	.keepalive		= tcp_set_keepalive,  	.recvmsg		= tcp_recvmsg,  	.sendmsg		= tcp_sendmsg,  	.sendpage		= tcp_sendpage, @@ -1944,7 +1960,7 @@ static void __net_exit tcpv6_net_exit(struct net *net)  static void __net_exit tcpv6_net_exit_batch(struct list_head *net_exit_list)  { -	inet_twsk_purge(&tcp_hashinfo, &tcp_death_row, AF_INET6); +	inet_twsk_purge(&tcp_hashinfo, AF_INET6);  }  static struct pernet_operations tcpv6_net_ops = {  | 
