Skip to content
Snippets Groups Projects
tcp_input.c 153 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    		 * never scaled.
    		 */
    		tp->snd_wnd    = ntohs(th->window);
    		tp->snd_wl1    = TCP_SKB_CB(skb)->seq;
    		tp->max_window = tp->snd_wnd;
    
    		TCP_ECN_rcv_syn(tp, th);
    
    
    John Heffner's avatar
    John Heffner committed
    		tcp_mtup_init(sk);
    
    		tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		tcp_initialize_rcv_mss(sk);
    
    
    		tcp_send_synack(sk);
    #if 0
    		/* Note, we could accept data and URG from this segment.
    		 * There are no obstacles to make this.
    		 *
    		 * However, if we ignore data in ACKless segments sometimes,
    		 * we have no reasons to accept it sometimes.
    		 * Also, seems the code doing it in step6 of tcp_rcv_state_process
    		 * is not flawless. So, discard packet for sanity.
    		 * Uncomment this return to process the data.
    		 */
    		return -1;
    #else
    		goto discard;
    #endif
    	}
    	/* "fifth, if neither of the SYN or RST bits is set then
    	 * drop the segment and return."
    	 */
    
    discard_and_undo:
    	tcp_clear_options(&tp->rx_opt);
    	tp->rx_opt.mss_clamp = saved_clamp;
    	goto discard;
    
    reset_and_undo:
    	tcp_clear_options(&tp->rx_opt);
    	tp->rx_opt.mss_clamp = saved_clamp;
    	return 1;
    }
    
    
    /*
     *	This function implements the receiving procedure of RFC 793 for
    
     *	all states except ESTABLISHED and TIME_WAIT.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *	It's called from both tcp_v4_rcv and tcp_v6_rcv and should be
     *	address independent.
     */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
    			  struct tcphdr *th, unsigned len)
    {
    	struct tcp_sock *tp = tcp_sk(sk);
    
    	struct inet_connection_sock *icsk = inet_csk(sk);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int queued = 0;
    
    	tp->rx_opt.saw_tstamp = 0;
    
    	switch (sk->sk_state) {
    	case TCP_CLOSE:
    		goto discard;
    
    	case TCP_LISTEN:
    
    		if (th->ack)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			return 1;
    
    
    		if (th->rst)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			goto discard;
    
    
    		if (th->syn) {
    
    			if (icsk->icsk_af_ops->conn_request(sk, skb) < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				return 1;
    
    
    			/* Now we have several options: In theory there is
    			 * nothing else in the frame. KA9Q has an option to
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			 * send data with the syn, BSD accepts data with the
    
    			 * syn up to the [to be] advertised window and
    			 * Solaris 2.1 gives you a protocol error. For now
    			 * we just ignore it, that fits the spec precisely
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			 * and avoids incompatibilities. It would be nice in
    			 * future to drop through and process the data.
    			 *
    
    			 * Now that TTCP is starting to be used we ought to
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			 * queue this data.
    			 * But, this leaves one open to an easy denial of
    
    			 * service attack, and SYN cookies can't defend
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			 * against this problem. So, we drop the data
    
    			 * in the interest of security over speed unless
    			 * it's still in use.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			 */
    
    			kfree_skb(skb);
    			return 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		}
    		goto discard;
    
    	case TCP_SYN_SENT:
    		queued = tcp_rcv_synsent_state_process(sk, skb, th, len);
    		if (queued >= 0)
    			return queued;
    
    		/* Do step6 onward by hand. */
    		tcp_urg(sk, skb, th);
    		__kfree_skb(skb);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return 0;
    	}
    
    	if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp &&
    
    	    tcp_paws_discard(sk, skb)) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		if (!th->rst) {
    			NET_INC_STATS_BH(LINUX_MIB_PAWSESTABREJECTED);
    			tcp_send_dupack(sk, skb);
    			goto discard;
    		}
    		/* Reset is accepted even if it did not pass PAWS. */
    	}
    
    	/* step 1: check sequence number */
    	if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
    		if (!th->rst)
    			tcp_send_dupack(sk, skb);
    		goto discard;
    	}
    
    	/* step 2: check RST bit */
    
    	if (th->rst) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		tcp_reset(sk);
    		goto discard;
    	}
    
    	tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
    
    	/* step 3: check security and precedence [ignored] */
    
    	/*	step 4:
    	 *
    	 *	Check for a SYN in window.
    	 */
    	if (th->syn && !before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
    		NET_INC_STATS_BH(LINUX_MIB_TCPABORTONSYN);
    		tcp_reset(sk);
    		return 1;
    	}
    
    	/* step 5: check the ACK field */
    	if (th->ack) {
    		int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH);
    
    
    		switch (sk->sk_state) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		case TCP_SYN_RECV:
    			if (acceptable) {
    				tp->copied_seq = tp->rcv_nxt;
    
    				smp_mb();
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				tcp_set_state(sk, TCP_ESTABLISHED);
    				sk->sk_state_change(sk);
    
    				/* Note, that this wakeup is only for marginal
    				 * crossed SYN case. Passively open sockets
    				 * are not waked up, because sk->sk_sleep ==
    				 * NULL and sk->sk_socket == NULL.
    				 */
    
    				if (sk->sk_socket)
    					sk_wake_async(sk,
    							SOCK_WAKE_IO, POLL_OUT);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    				tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
    				tp->snd_wnd = ntohs(th->window) <<
    					      tp->rx_opt.snd_wscale;
    				tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq,
    					    TCP_SKB_CB(skb)->seq);
    
    				/* tcp_ack considers this ACK as duplicate
    				 * and does not calculate rtt.
    				 * Fix it at least with timestamps.
    				 */
    				if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
    				    !tp->srtt)
    
    					tcp_ack_saw_tstamp(sk, 0);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    				if (tp->rx_opt.tstamp_ok)
    					tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
    
    				/* Make sure socket is routed, for
    				 * correct metrics.
    				 */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    				tcp_init_metrics(sk);
    
    
    				tcp_init_congestion_control(sk);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				/* Prevent spurious tcp_cwnd_restart() on
    				 * first data packet.
    				 */
    				tp->lsndtime = tcp_time_stamp;
    
    
    John Heffner's avatar
    John Heffner committed
    				tcp_mtup_init(sk);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				tcp_initialize_rcv_mss(sk);
    				tcp_init_buffer_space(sk);
    				tcp_fast_path_on(tp);
    			} else {
    				return 1;
    			}
    			break;
    
    		case TCP_FIN_WAIT1:
    			if (tp->snd_una == tp->write_seq) {
    				tcp_set_state(sk, TCP_FIN_WAIT2);
    				sk->sk_shutdown |= SEND_SHUTDOWN;
    				dst_confirm(sk->sk_dst_cache);
    
    				if (!sock_flag(sk, SOCK_DEAD))
    					/* Wake up lingering close() */
    					sk->sk_state_change(sk);
    				else {
    					int tmo;
    
    					if (tp->linger2 < 0 ||
    					    (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
    					     after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt))) {
    						tcp_done(sk);
    						NET_INC_STATS_BH(LINUX_MIB_TCPABORTONDATA);
    						return 1;
    					}
    
    
    					tmo = tcp_fin_time(sk);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    					if (tmo > TCP_TIMEWAIT_LEN) {
    
    						inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    					} else if (th->fin || sock_owned_by_user(sk)) {
    						/* Bad case. We could lose such FIN otherwise.
    						 * It is not a big problem, but it looks confusing
    						 * and not so rare event. We still can lose it now,
    						 * if it spins in bh_lock_sock(), but it is really
    						 * marginal case.
    						 */
    
    						inet_csk_reset_keepalive_timer(sk, tmo);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    					} else {
    						tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
    						goto discard;
    					}
    				}
    			}
    			break;
    
    		case TCP_CLOSING:
    			if (tp->snd_una == tp->write_seq) {
    				tcp_time_wait(sk, TCP_TIME_WAIT, 0);
    				goto discard;
    			}
    			break;
    
    		case TCP_LAST_ACK:
    			if (tp->snd_una == tp->write_seq) {
    				tcp_update_metrics(sk);
    				tcp_done(sk);
    				goto discard;
    			}
    			break;
    		}
    	} else
    		goto discard;
    
    	/* step 6: check the URG bit */
    	tcp_urg(sk, skb, th);
    
    	/* step 7: process the segment text */
    	switch (sk->sk_state) {
    	case TCP_CLOSE_WAIT:
    	case TCP_CLOSING:
    	case TCP_LAST_ACK:
    		if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
    			break;
    	case TCP_FIN_WAIT1:
    	case TCP_FIN_WAIT2:
    		/* RFC 793 says to queue data in these states,
    
    		 * RFC 1122 says we MUST send a reset.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		 * BSD 4.4 also does reset.
    		 */
    		if (sk->sk_shutdown & RCV_SHUTDOWN) {
    			if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
    			    after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) {
    				NET_INC_STATS_BH(LINUX_MIB_TCPABORTONDATA);
    				tcp_reset(sk);
    				return 1;
    			}
    		}
    		/* Fall through */
    
    	case TCP_ESTABLISHED:
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		tcp_data_queue(sk, skb);
    		queued = 1;
    		break;
    	}
    
    	/* tcp_data could move socket to TIME-WAIT */
    	if (sk->sk_state != TCP_CLOSE) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		tcp_ack_snd_check(sk);
    	}
    
    
    	if (!queued) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    discard:
    		__kfree_skb(skb);
    	}
    	return 0;
    }
    
    EXPORT_SYMBOL(sysctl_tcp_ecn);
    EXPORT_SYMBOL(sysctl_tcp_reordering);
    EXPORT_SYMBOL(tcp_parse_options);
    EXPORT_SYMBOL(tcp_rcv_established);
    EXPORT_SYMBOL(tcp_rcv_state_process);
    
    Stephen Hemminger's avatar
    Stephen Hemminger committed
    EXPORT_SYMBOL(tcp_initialize_rcv_mss);