diff options
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
| -rw-r--r-- | net/ipv4/tcp_ipv4.c | 27 | 
1 files changed, 23 insertions, 4 deletions
| diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 277d71239d75..af81e4a6a8d8 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1673,7 +1673,9 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)  	if (TCP_SKB_CB(tail)->end_seq != TCP_SKB_CB(skb)->seq ||  	    TCP_SKB_CB(tail)->ip_dsfield != TCP_SKB_CB(skb)->ip_dsfield ||  	    ((TCP_SKB_CB(tail)->tcp_flags | -	      TCP_SKB_CB(skb)->tcp_flags) & TCPHDR_URG) || +	      TCP_SKB_CB(skb)->tcp_flags) & (TCPHDR_SYN | TCPHDR_RST | TCPHDR_URG)) || +	    !((TCP_SKB_CB(tail)->tcp_flags & +	      TCP_SKB_CB(skb)->tcp_flags) & TCPHDR_ACK) ||  	    ((TCP_SKB_CB(tail)->tcp_flags ^  	      TCP_SKB_CB(skb)->tcp_flags) & (TCPHDR_ECE | TCPHDR_CWR)) ||  #ifdef CONFIG_TLS_DEVICE @@ -1692,6 +1694,15 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)  		if (after(TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(tail)->ack_seq))  			TCP_SKB_CB(tail)->ack_seq = TCP_SKB_CB(skb)->ack_seq; +		/* We have to update both TCP_SKB_CB(tail)->tcp_flags and +		 * thtail->fin, so that the fast path in tcp_rcv_established() +		 * is not entered if we append a packet with a FIN. +		 * SYN, RST, URG are not present. +		 * ACK is set on both packets. +		 * PSH : we do not really care in TCP stack, +		 *       at least for 'GRO' packets. +		 */ +		thtail->fin |= th->fin;  		TCP_SKB_CB(tail)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags;  		if (TCP_SKB_CB(skb)->has_rxtstamp) { @@ -1774,6 +1785,7 @@ static void tcp_v4_fill_cb(struct sk_buff *skb, const struct iphdr *iph,  int tcp_v4_rcv(struct sk_buff *skb)  {  	struct net *net = dev_net(skb->dev); +	struct sk_buff *skb_to_free;  	int sdif = inet_sdif(skb);  	const struct iphdr *iph;  	const struct tcphdr *th; @@ -1905,11 +1917,17 @@ process:  	tcp_segs_in(tcp_sk(sk), skb);  	ret = 0;  	if (!sock_owned_by_user(sk)) { +		skb_to_free = sk->sk_rx_skb_cache; +		sk->sk_rx_skb_cache = NULL;  		ret = tcp_v4_do_rcv(sk, skb); -	} else if (tcp_add_backlog(sk, skb)) { -		goto discard_and_relse; +	} else { +		if (tcp_add_backlog(sk, skb)) +			goto discard_and_relse; +		skb_to_free = NULL;  	}  	bh_unlock_sock(sk); +	if (skb_to_free) +		__kfree_skb(skb_to_free);  put_and_return:  	if (refcounted) @@ -2578,7 +2596,8 @@ static void __net_exit tcp_sk_exit(struct net *net)  {  	int cpu; -	module_put(net->ipv4.tcp_congestion_control->owner); +	if (net->ipv4.tcp_congestion_control) +		module_put(net->ipv4.tcp_congestion_control->owner);  	for_each_possible_cpu(cpu)  		inet_ctl_sock_destroy(*per_cpu_ptr(net->ipv4.tcp_sk, cpu)); |