diff options
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
| -rw-r--r-- | net/ipv4/tcp_ipv4.c | 242 | 
1 files changed, 75 insertions, 167 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 77cccda1ad0c..cd17f009aede 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -99,7 +99,7 @@ static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,  struct inet_hashinfo tcp_hashinfo;  EXPORT_SYMBOL(tcp_hashinfo); -static inline __u32 tcp_v4_init_sequence(const struct sk_buff *skb) +static  __u32 tcp_v4_init_sequence(const struct sk_buff *skb)  {  	return secure_tcp_sequence_number(ip_hdr(skb)->daddr,  					  ip_hdr(skb)->saddr, @@ -208,6 +208,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)  	inet->inet_dport = usin->sin_port;  	inet->inet_daddr = daddr; +	inet_set_txhash(sk); +  	inet_csk(sk)->icsk_ext_hdr_len = 0;  	if (inet_opt)  		inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; @@ -269,7 +271,7 @@ EXPORT_SYMBOL(tcp_v4_connect);   * It can be called through tcp_release_cb() if socket was owned by user   * at the time tcp_v4_err() was called to handle ICMP message.   */ -static void tcp_v4_mtu_reduced(struct sock *sk) +void tcp_v4_mtu_reduced(struct sock *sk)  {  	struct dst_entry *dst;  	struct inet_sock *inet = inet_sk(sk); @@ -300,6 +302,7 @@ static void tcp_v4_mtu_reduced(struct sock *sk)  		tcp_simple_retransmit(sk);  	} /* else let the usual retransmit timer handle it */  } +EXPORT_SYMBOL(tcp_v4_mtu_reduced);  static void do_redirect(struct sk_buff *skb, struct sock *sk)  { @@ -342,11 +345,6 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)  	int err;  	struct net *net = dev_net(icmp_skb->dev); -	if (icmp_skb->len < (iph->ihl << 2) + 8) { -		ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS); -		return; -	} -  	sk = inet_lookup(net, &tcp_hashinfo, iph->daddr, th->dest,  			iph->saddr, th->source, inet_iif(icmp_skb));  	if (!sk) { @@ -814,6 +812,7 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,   *	socket.   */  static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, +			      struct flowi *fl,  			      struct request_sock *req,  			      u16 queue_mapping,  			      struct tcp_fastopen_cookie *foc) @@ -837,24 +836,11 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,  					    ireq->ir_rmt_addr,  					    ireq->opt);  		err = net_xmit_eval(err); -		if (!tcp_rsk(req)->snt_synack && !err) -			tcp_rsk(req)->snt_synack = tcp_time_stamp;  	}  	return err;  } -static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req) -{ -	int res = tcp_v4_send_synack(sk, NULL, 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; -} -  /*   *	IPv4 request_sock destructor.   */ @@ -1064,7 +1050,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,  	if (sin->sin_family != AF_INET)  		return -EINVAL; -	if (!cmd.tcpm_key || !cmd.tcpm_keylen) +	if (!cmd.tcpm_keylen)  		return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,  				      AF_INET); @@ -1182,7 +1168,8 @@ clear_hash_noput:  }  EXPORT_SYMBOL(tcp_v4_md5_hash_skb); -static bool tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb) +static bool __tcp_v4_inbound_md5_hash(struct sock *sk, +				      const struct sk_buff *skb)  {  	/*  	 * This gets called for each TCP segment that arrives @@ -1235,163 +1222,81 @@ static bool tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)  	return false;  } +static bool tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb) +{ +	bool ret; + +	rcu_read_lock(); +	ret = __tcp_v4_inbound_md5_hash(sk, skb); +	rcu_read_unlock(); + +	return ret; +} +  #endif +static void tcp_v4_init_req(struct request_sock *req, struct sock *sk, +			    struct sk_buff *skb) +{ +	struct inet_request_sock *ireq = inet_rsk(req); + +	ireq->ir_loc_addr = ip_hdr(skb)->daddr; +	ireq->ir_rmt_addr = ip_hdr(skb)->saddr; +	ireq->no_srccheck = inet_sk(sk)->transparent; +	ireq->opt = tcp_v4_save_options(skb); +} + +static struct dst_entry *tcp_v4_route_req(struct sock *sk, struct flowi *fl, +					  const struct request_sock *req, +					  bool *strict) +{ +	struct dst_entry *dst = inet_csk_route_req(sk, &fl->u.ip4, req); + +	if (strict) { +		if (fl->u.ip4.daddr == inet_rsk(req)->ir_rmt_addr) +			*strict = true; +		else +			*strict = false; +	} + +	return dst; +} +  struct request_sock_ops tcp_request_sock_ops __read_mostly = {  	.family		=	PF_INET,  	.obj_size	=	sizeof(struct tcp_request_sock), -	.rtx_syn_ack	=	tcp_v4_rtx_synack, +	.rtx_syn_ack	=	tcp_rtx_synack,  	.send_ack	=	tcp_v4_reqsk_send_ack,  	.destructor	=	tcp_v4_reqsk_destructor,  	.send_reset	=	tcp_v4_send_reset,  	.syn_ack_timeout = 	tcp_syn_ack_timeout,  }; -#ifdef CONFIG_TCP_MD5SIG  static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { +	.mss_clamp	=	TCP_MSS_DEFAULT, +#ifdef CONFIG_TCP_MD5SIG  	.md5_lookup	=	tcp_v4_reqsk_md5_lookup,  	.calc_md5_hash	=	tcp_v4_md5_hash_skb, -};  #endif +	.init_req	=	tcp_v4_init_req, +#ifdef CONFIG_SYN_COOKIES +	.cookie_init_seq =	cookie_v4_init_sequence, +#endif +	.route_req	=	tcp_v4_route_req, +	.init_seq	=	tcp_v4_init_sequence, +	.send_synack	=	tcp_v4_send_synack, +	.queue_hash_add =	inet_csk_reqsk_queue_hash_add, +};  int tcp_v4_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 tcp_sock *tp = tcp_sk(sk); -	struct dst_entry *dst = NULL; -	__be32 saddr = ip_hdr(skb)->saddr; -	__be32 daddr = ip_hdr(skb)->daddr; -	__u32 isn = TCP_SKB_CB(skb)->when; -	bool want_cookie = false, fastopen; -	struct flowi4 fl4; -	struct tcp_fastopen_cookie foc = { .len = -1 }; -	int err; -  	/* Never answer to SYNs send to broadcast or multicast */  	if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))  		goto drop; -	/* TW buckets are converted to open requests without -	 * limitations, they conserve resources and peer is -	 * evidently real one. -	 */ -	if ((sysctl_tcp_syncookies == 2 || -	     inet_csk_reqsk_queue_is_full(sk)) && !isn) { -		want_cookie = tcp_syn_flood_action(sk, skb, "TCP"); -		if (!want_cookie) -			goto drop; -	} - -	/* Accept backlog is full. If we have already queued enough -	 * of warm entries in syn queue, drop request. It is better than -	 * clogging syn queue with openreqs with exponentially increasing -	 * timeout. -	 */ -	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 = inet_reqsk_alloc(&tcp_request_sock_ops); -	if (!req) -		goto drop; - -#ifdef CONFIG_TCP_MD5SIG -	tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops; -#endif - -	tcp_clear_options(&tmp_opt); -	tmp_opt.mss_clamp = TCP_MSS_DEFAULT; -	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_loc_addr = daddr; -	ireq->ir_rmt_addr = saddr; -	ireq->no_srccheck = inet_sk(sk)->transparent; -	ireq->opt = tcp_v4_save_options(skb); -	ireq->ir_mark = inet_request_mark(sk, skb); - -	if (security_inet_conn_request(sk, skb, req)) -		goto drop_and_free; - -	if (!want_cookie || tmp_opt.tstamp_ok) -		TCP_ECN_create_request(req, skb, sock_net(sk)); - -	if (want_cookie) { -		isn = cookie_v4_init_sequence(sk, skb, &req->mss); -		req->cookie_ts = tmp_opt.tstamp_ok; -	} else if (!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 = inet_csk_route_req(sk, &fl4, req)) != NULL && -		    fl4.daddr == saddr) { -			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 pr_fmt("drop open request from %pI4/%u\n"), -				       &saddr, ntohs(tcp_hdr(skb)->source)); -			goto drop_and_release; -		} - -		isn = tcp_v4_init_sequence(skb); -	} -	if (!dst && (dst = inet_csk_route_req(sk, &fl4, 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_v4_send_synack(sk, dst, req, -				 skb_get_queue_mapping(skb), &foc); -	if (!fastopen) { -		if (err || want_cookie) -			goto drop_and_free; +	return tcp_conn_request(&tcp_request_sock_ops, +				&tcp_request_sock_ipv4_ops, sk, skb); -		tcp_rsk(req)->snt_synack = tcp_time_stamp; -		tcp_rsk(req)->listener = NULL; -		inet_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; @@ -1439,6 +1344,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,  	newinet->mc_ttl	      = ip_hdr(skb)->ttl;  	newinet->rcv_tos      = ip_hdr(skb)->tos;  	inet_csk(newsk)->icsk_ext_hdr_len = 0; +	inet_set_txhash(newsk);  	if (inet_opt)  		inet_csk(newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen;  	newinet->inet_id = newtp->write_seq ^ jiffies; @@ -1539,16 +1445,6 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)  int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)  {  	struct sock *rsk; -#ifdef CONFIG_TCP_MD5SIG -	/* -	 * We really want to reject the packet as early as possible -	 * if: -	 *  o We're expecting an MD5'd packet and this is no MD5 tcp option -	 *  o There is an MD5 option and we're not expecting one -	 */ -	if (tcp_v4_inbound_md5_hash(sk, skb)) -		goto discard; -#endif  	if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */  		struct dst_entry *dst = sk->sk_rx_dst; @@ -1751,6 +1647,18 @@ process:  	if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))  		goto discard_and_relse; + +#ifdef CONFIG_TCP_MD5SIG +	/* +	 * We really want to reject the packet as early as possible +	 * if: +	 *  o We're expecting an MD5'd packet and this is no MD5 tcp option +	 *  o There is an MD5 option and we're not expecting one +	 */ +	if (tcp_v4_inbound_md5_hash(sk, skb)) +		goto discard_and_relse; +#endif +  	nf_reset(skb);  	if (sk_filter(sk, skb)) @@ -1880,6 +1788,7 @@ const struct inet_connection_sock_af_ops ipv4_specific = {  	.compat_setsockopt = compat_ip_setsockopt,  	.compat_getsockopt = compat_ip_getsockopt,  #endif +	.mtu_reduced	   = tcp_v4_mtu_reduced,  };  EXPORT_SYMBOL(ipv4_specific); @@ -2499,7 +2408,6 @@ struct proto tcp_prot = {  	.sendpage		= tcp_sendpage,  	.backlog_rcv		= tcp_v4_do_rcv,  	.release_cb		= tcp_release_cb, -	.mtu_reduced		= tcp_v4_mtu_reduced,  	.hash			= inet_hash,  	.unhash			= inet_unhash,  	.get_port		= inet_csk_get_port,  |