diff options
Diffstat (limited to 'net/ipv6/tcp_ipv6.c')
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 237 | 
1 files changed, 76 insertions, 161 deletions
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 229239ad96b1..29964c3d363c 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -198,6 +198,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  	sk->sk_v6_daddr = usin->sin6_addr;  	np->flow_label = fl6.flowlabel; +	ip6_set_txhash(sk); +  	/*  	 *	TCP over IPv4  	 */ @@ -470,13 +472,14 @@ out:  static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, -			      struct flowi6 *fl6, +			      struct flowi *fl,  			      struct request_sock *req,  			      u16 queue_mapping,  			      struct tcp_fastopen_cookie *foc)  {  	struct inet_request_sock *ireq = inet_rsk(req);  	struct ipv6_pinfo *np = inet6_sk(sk); +	struct flowi6 *fl6 = &fl->u.ip6;  	struct sk_buff *skb;  	int err = -ENOMEM; @@ -503,18 +506,6 @@ done:  	return err;  } -static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req) -{ -	struct flowi6 fl6; -	int res; - -	res = tcp_v6_send_synack(sk, NULL, &fl6, req, 0, NULL); -	if (!res) { -		TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); -		NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS); -	} -	return res; -}  static void tcp_v6_reqsk_destructor(struct request_sock *req)  { @@ -676,7 +667,8 @@ clear_hash_noput:  	return 1;  } -static int tcp_v6_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb) +static int __tcp_v6_inbound_md5_hash(struct sock *sk, +				     const struct sk_buff *skb)  {  	const __u8 *hash_location = NULL;  	struct tcp_md5sig_key *hash_expected; @@ -716,24 +708,80 @@ static int tcp_v6_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)  	}  	return 0;  } + +static int tcp_v6_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb) +{ +	int ret; + +	rcu_read_lock(); +	ret = __tcp_v6_inbound_md5_hash(sk, skb); +	rcu_read_unlock(); + +	return ret; +} +  #endif +static void tcp_v6_init_req(struct request_sock *req, struct sock *sk, +			    struct sk_buff *skb) +{ +	struct inet_request_sock *ireq = inet_rsk(req); +	struct ipv6_pinfo *np = inet6_sk(sk); + +	ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; +	ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; + +	ireq->ir_iif = sk->sk_bound_dev_if; + +	/* So that link locals have meaning */ +	if (!sk->sk_bound_dev_if && +	    ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) +		ireq->ir_iif = inet6_iif(skb); + +	if (!TCP_SKB_CB(skb)->when && +	    (ipv6_opt_accepted(sk, skb) || np->rxopt.bits.rxinfo || +	     np->rxopt.bits.rxoinfo || np->rxopt.bits.rxhlim || +	     np->rxopt.bits.rxohlim || np->repflow)) { +		atomic_inc(&skb->users); +		ireq->pktopts = skb; +	} +} + +static struct dst_entry *tcp_v6_route_req(struct sock *sk, struct flowi *fl, +					  const struct request_sock *req, +					  bool *strict) +{ +	if (strict) +		*strict = true; +	return inet6_csk_route_req(sk, &fl->u.ip6, req); +} +  struct request_sock_ops tcp6_request_sock_ops __read_mostly = {  	.family		=	AF_INET6,  	.obj_size	=	sizeof(struct tcp6_request_sock), -	.rtx_syn_ack	=	tcp_v6_rtx_synack, +	.rtx_syn_ack	=	tcp_rtx_synack,  	.send_ack	=	tcp_v6_reqsk_send_ack,  	.destructor	=	tcp_v6_reqsk_destructor,  	.send_reset	=	tcp_v6_send_reset,  	.syn_ack_timeout =	tcp_syn_ack_timeout,  }; -#ifdef CONFIG_TCP_MD5SIG  static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { +	.mss_clamp	=	IPV6_MIN_MTU - sizeof(struct tcphdr) - +				sizeof(struct ipv6hdr), +#ifdef CONFIG_TCP_MD5SIG  	.md5_lookup	=	tcp_v6_reqsk_md5_lookup,  	.calc_md5_hash	=	tcp_v6_md5_hash_skb, -};  #endif +	.init_req	=	tcp_v6_init_req, +#ifdef CONFIG_SYN_COOKIES +	.cookie_init_seq =	cookie_v6_init_sequence, +#endif +	.route_req	=	tcp_v6_route_req, +	.init_seq	=	tcp_v6_init_sequence, +	.send_synack	=	tcp_v6_send_synack, +	.queue_hash_add =	inet6_csk_reqsk_queue_hash_add, +};  static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,  				 u32 tsval, u32 tsecr, int oif, @@ -973,153 +1021,17 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb)  	return sk;  } -/* FIXME: this is substantially similar to the ipv4 code. - * Can some kind of merge be done? -- erics - */  static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)  { -	struct tcp_options_received tmp_opt; -	struct request_sock *req; -	struct inet_request_sock *ireq; -	struct ipv6_pinfo *np = inet6_sk(sk); -	struct tcp_sock *tp = tcp_sk(sk); -	__u32 isn = TCP_SKB_CB(skb)->when; -	struct dst_entry *dst = NULL; -	struct tcp_fastopen_cookie foc = { .len = -1 }; -	bool want_cookie = false, fastopen; -	struct flowi6 fl6; -	int err; -  	if (skb->protocol == htons(ETH_P_IP))  		return tcp_v4_conn_request(sk, skb);  	if (!ipv6_unicast_destination(skb))  		goto drop; -	if ((sysctl_tcp_syncookies == 2 || -	     inet_csk_reqsk_queue_is_full(sk)) && !isn) { -		want_cookie = tcp_syn_flood_action(sk, skb, "TCPv6"); -		if (!want_cookie) -			goto drop; -	} - -	if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) { -		NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); -		goto drop; -	} - -	req = inet6_reqsk_alloc(&tcp6_request_sock_ops); -	if (req == NULL) -		goto drop; - -#ifdef CONFIG_TCP_MD5SIG -	tcp_rsk(req)->af_specific = &tcp_request_sock_ipv6_ops; -#endif - -	tcp_clear_options(&tmp_opt); -	tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); -	tmp_opt.user_mss = tp->rx_opt.user_mss; -	tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc); - -	if (want_cookie && !tmp_opt.saw_tstamp) -		tcp_clear_options(&tmp_opt); - -	tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; -	tcp_openreq_init(req, &tmp_opt, skb); - -	ireq = inet_rsk(req); -	ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; -	ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; -	if (!want_cookie || tmp_opt.tstamp_ok) -		TCP_ECN_create_request(req, skb, sock_net(sk)); - -	ireq->ir_iif = sk->sk_bound_dev_if; -	ireq->ir_mark = inet_request_mark(sk, skb); - -	/* So that link locals have meaning */ -	if (!sk->sk_bound_dev_if && -	    ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) -		ireq->ir_iif = inet6_iif(skb); - -	if (!isn) { -		if (ipv6_opt_accepted(sk, skb) || -		    np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || -		    np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim || -		    np->repflow) { -			atomic_inc(&skb->users); -			ireq->pktopts = skb; -		} +	return tcp_conn_request(&tcp6_request_sock_ops, +				&tcp_request_sock_ipv6_ops, sk, skb); -		if (want_cookie) { -			isn = cookie_v6_init_sequence(sk, skb, &req->mss); -			req->cookie_ts = tmp_opt.tstamp_ok; -			goto have_isn; -		} - -		/* VJ's idea. We save last timestamp seen -		 * from the destination in peer table, when entering -		 * state TIME-WAIT, and check against it before -		 * accepting new connection request. -		 * -		 * If "isn" is not zero, this request hit alive -		 * timewait bucket, so that all the necessary checks -		 * are made in the function processing timewait state. -		 */ -		if (tmp_opt.saw_tstamp && -		    tcp_death_row.sysctl_tw_recycle && -		    (dst = inet6_csk_route_req(sk, &fl6, req)) != NULL) { -			if (!tcp_peer_is_proven(req, dst, true)) { -				NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); -				goto drop_and_release; -			} -		} -		/* Kill the following clause, if you dislike this way. */ -		else if (!sysctl_tcp_syncookies && -			 (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < -			  (sysctl_max_syn_backlog >> 2)) && -			 !tcp_peer_is_proven(req, dst, false)) { -			/* Without syncookies last quarter of -			 * backlog is filled with destinations, -			 * proven to be alive. -			 * It means that we continue to communicate -			 * to destinations, already remembered -			 * to the moment of synflood. -			 */ -			LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open request from %pI6/%u\n", -				       &ireq->ir_v6_rmt_addr, ntohs(tcp_hdr(skb)->source)); -			goto drop_and_release; -		} - -		isn = tcp_v6_init_sequence(skb); -	} -have_isn: - -	if (security_inet_conn_request(sk, skb, req)) -		goto drop_and_release; - -	if (!dst && (dst = inet6_csk_route_req(sk, &fl6, req)) == NULL) -		goto drop_and_free; - -	tcp_rsk(req)->snt_isn = isn; -	tcp_rsk(req)->snt_synack = tcp_time_stamp; -	tcp_openreq_init_rwin(req, sk, dst); -	fastopen = !want_cookie && -		   tcp_try_fastopen(sk, skb, req, &foc, dst); -	err = tcp_v6_send_synack(sk, dst, &fl6, req, -				 skb_get_queue_mapping(skb), &foc); -	if (!fastopen) { -		if (err || want_cookie) -			goto drop_and_free; - -		tcp_rsk(req)->listener = NULL; -		inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); -	} -	return 0; - -drop_and_release: -	dst_release(dst); -drop_and_free: -	reqsk_free(req);  drop:  	NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);  	return 0; /* don't send reset */ @@ -1235,6 +1147,8 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,  	newsk->sk_v6_rcv_saddr = ireq->ir_v6_loc_addr;  	newsk->sk_bound_dev_if = ireq->ir_iif; +	ip6_set_txhash(newsk); +  	/* Now IPv6 options...  	   First: no IPv4 options. @@ -1346,11 +1260,6 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)  	if (skb->protocol == htons(ETH_P_IP))  		return tcp_v4_do_rcv(sk, skb); -#ifdef CONFIG_TCP_MD5SIG -	if (tcp_v6_inbound_md5_hash(sk, skb)) -		goto discard; -#endif -  	if (sk_filter(sk, skb))  		goto discard; @@ -1523,6 +1432,11 @@ process:  	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))  		goto discard_and_relse; +#ifdef CONFIG_TCP_MD5SIG +	if (tcp_v6_inbound_md5_hash(sk, skb)) +		goto discard_and_relse; +#endif +  	if (sk_filter(sk, skb))  		goto discard_and_relse; @@ -1681,6 +1595,7 @@ static const struct inet_connection_sock_af_ops ipv6_specific = {  	.compat_setsockopt = compat_ipv6_setsockopt,  	.compat_getsockopt = compat_ipv6_getsockopt,  #endif +	.mtu_reduced	   = tcp_v6_mtu_reduced,  };  #ifdef CONFIG_TCP_MD5SIG @@ -1711,6 +1626,7 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = {  	.compat_setsockopt = compat_ipv6_setsockopt,  	.compat_getsockopt = compat_ipv6_getsockopt,  #endif +	.mtu_reduced	   = tcp_v4_mtu_reduced,  };  #ifdef CONFIG_TCP_MD5SIG @@ -1950,7 +1866,6 @@ struct proto tcpv6_prot = {  	.sendpage		= tcp_sendpage,  	.backlog_rcv		= tcp_v6_do_rcv,  	.release_cb		= tcp_release_cb, -	.mtu_reduced		= tcp_v6_mtu_reduced,  	.hash			= tcp_v6_hash,  	.unhash			= inet_unhash,  	.get_port		= inet_csk_get_port,  |