tcp_input.c

来自「eCos操作系统源码」· C语言 代码 · 共 2,238 行 · 第 1/5 页

C
2,238
字号
		/* fall into ... */	/*	 * In ESTABLISHED state: drop duplicate ACKs; ACK out of range	 * ACKs.  If the ack is in the range	 *	tp->snd_una < th->th_ack <= tp->snd_max	 * then advance tp->snd_una to th->th_ack and drop	 * data from the retransmission queue.  If this ACK reflects	 * more up to date window information we update our window information.	 */	case TCPS_ESTABLISHED:	case TCPS_FIN_WAIT_1:	case TCPS_FIN_WAIT_2:	case TCPS_CLOSE_WAIT:	case TCPS_CLOSING:	case TCPS_LAST_ACK:	case TCPS_TIME_WAIT:		if (SEQ_LEQ(th->th_ack, tp->snd_una)) {			if (tlen == 0 && tiwin == tp->snd_wnd) {				tcpstat.tcps_rcvdupack++;				/*				 * If we have outstanding data (other than				 * a window probe), this is a completely				 * duplicate ack (ie, window info didn't				 * change), the ack is the biggest we've				 * seen and we've seen exactly our rexmt				 * threshhold of them, assume a packet				 * has been dropped and retransmit it.				 * Kludge snd_nxt & the congestion				 * window so we send only this one				 * packet.				 *				 * We know we're losing at the current				 * window size so do congestion avoidance				 * (set ssthresh to half the current window				 * and pull our congestion window back to				 * the new ssthresh).				 *				 * Dup acks mean that packets have left the				 * network (they're now cached at the receiver)				 * so bump cwnd by the amount in the receiver				 * to keep a constant cwnd packets in the				 * network.				 */				if (!callout_active(tp->tt_rexmt) ||				    th->th_ack != tp->snd_una)					tp->t_dupacks = 0;				else if (++tp->t_dupacks == tcprexmtthresh) {					tcp_seq onxt = tp->snd_nxt;					u_int win =					    min(tp->snd_wnd, tp->snd_cwnd) / 2 /						tp->t_maxseg;					if (tcp_do_newreno && SEQ_LT(th->th_ack,					    tp->snd_recover)) {						/* False retransmit, should not						 * cut window						 */						tp->snd_cwnd += tp->t_maxseg;						tp->t_dupacks = 0;						(void) tcp_output(tp);						goto drop;					}					if (win < 2)						win = 2;					tp->snd_ssthresh = win * tp->t_maxseg;					tp->snd_recover = tp->snd_max;					callout_stop(tp->tt_rexmt);					tp->t_rtttime = 0;					tp->snd_nxt = th->th_ack;					tp->snd_cwnd = tp->t_maxseg;					(void) tcp_output(tp);					tp->snd_cwnd = tp->snd_ssthresh +					       tp->t_maxseg * tp->t_dupacks;					if (SEQ_GT(onxt, tp->snd_nxt))						tp->snd_nxt = onxt;					goto drop;				} else if (tp->t_dupacks > tcprexmtthresh) {					tp->snd_cwnd += tp->t_maxseg;					(void) tcp_output(tp);					goto drop;				}			} else				tp->t_dupacks = 0;			break;		}		/*		 * If the congestion window was inflated to account		 * for the other side's cached packets, retract it.		 */		if (tcp_do_newreno == 0) {                        if (tp->t_dupacks >= tcprexmtthresh &&                                tp->snd_cwnd > tp->snd_ssthresh)                                tp->snd_cwnd = tp->snd_ssthresh;                        tp->t_dupacks = 0;                } else if (tp->t_dupacks >= tcprexmtthresh &&		    !tcp_newreno(tp, th)) {                        /*                         * Window inflation should have left us with approx.                         * snd_ssthresh outstanding data.  But in case we                         * would be inclined to send a burst, better to do                         * it via the slow start mechanism.                         */			if (SEQ_GT(th->th_ack + tp->snd_ssthresh, tp->snd_max))                                tp->snd_cwnd =				    tp->snd_max - th->th_ack + tp->t_maxseg;			else                        	tp->snd_cwnd = tp->snd_ssthresh;                        tp->t_dupacks = 0;                }		if (SEQ_GT(th->th_ack, tp->snd_max)) {			tcpstat.tcps_rcvacktoomuch++;			goto dropafterack;		}		/*		 *  If we reach this point, ACK is not a duplicate,		 *     i.e., it ACKs something we sent.		 */		if (tp->t_flags & TF_NEEDSYN) {			/*			 * T/TCP: Connection was half-synchronized, and our			 * SYN has been ACK'd (so connection is now fully			 * synchronized).  Go to non-starred state,			 * increment snd_una for ACK of SYN, and check if			 * we can do window scaling.			 */			tp->t_flags &= ~TF_NEEDSYN;			tp->snd_una++;			/* Do window scaling? */			if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==				(TF_RCVD_SCALE|TF_REQ_SCALE)) {				tp->snd_scale = tp->requested_s_scale;				tp->rcv_scale = tp->request_r_scale;			}		}process_ACK:		acked = th->th_ack - tp->snd_una;		tcpstat.tcps_rcvackpack++;		tcpstat.tcps_rcvackbyte += acked;		/*		 * If we just performed our first retransmit, and the ACK		 * arrives within our recovery window, then it was a mistake		 * to do the retransmit in the first place.  Recover our		 * original cwnd and ssthresh, and proceed to transmit where		 * we left off.		 */		if (tp->t_rxtshift == 1 && ticks < tp->t_badrxtwin) {			tp->snd_cwnd = tp->snd_cwnd_prev;			tp->snd_ssthresh = tp->snd_ssthresh_prev;			tp->snd_nxt = tp->snd_max;			tp->t_badrxtwin = 0;	/* XXX probably not required */ 		}		/*		 * If we have a timestamp reply, update smoothed		 * round trip time.  If no timestamp is present but		 * transmit timer is running and timed sequence		 * number was acked, update smoothed round trip time.		 * Since we now have an rtt measurement, cancel the		 * timer backoff (cf., Phil Karn's retransmit alg.).		 * Recompute the initial retransmit timer.		 */		if (to.to_flag & TOF_TS)			tcp_xmit_timer(tp, ticks - to.to_tsecr + 1);		else if (tp->t_rtttime && SEQ_GT(th->th_ack, tp->t_rtseq))			tcp_xmit_timer(tp, ticks - tp->t_rtttime);		/*		 * If all outstanding data is acked, stop retransmit		 * timer and remember to restart (more output or persist).		 * If there is more data to be acked, restart retransmit		 * timer, using current (possibly backed-off) value.		 */		if (th->th_ack == tp->snd_max) {			callout_stop(tp->tt_rexmt);			needoutput = 1;		} else if (!callout_active(tp->tt_persist))			callout_reset(tp->tt_rexmt, tp->t_rxtcur,				      tcp_timer_rexmt, tp);		/*		 * If no data (only SYN) was ACK'd,		 *    skip rest of ACK processing.		 */		if (acked == 0)			goto step6;		/*		 * When new data is acked, open the congestion window.		 * If the window gives us less than ssthresh packets		 * in flight, open exponentially (maxseg per packet).		 * Otherwise open linearly: maxseg per window		 * (maxseg^2 / cwnd per packet).		 */		{		register u_int cw = tp->snd_cwnd;		register u_int incr = tp->t_maxseg;		if (cw > tp->snd_ssthresh)			incr = incr * incr / cw;		/*		 * If t_dupacks != 0 here, it indicates that we are still		 * in NewReno fast recovery mode, so we leave the congestion		 * window alone.		 */		if (tcp_do_newreno == 0 || tp->t_dupacks == 0)			tp->snd_cwnd = min(cw + incr,TCP_MAXWIN<<tp->snd_scale);		}		if (acked > so->so_snd.sb_cc) {			tp->snd_wnd -= so->so_snd.sb_cc;			sbdrop(&so->so_snd, (int)so->so_snd.sb_cc);			ourfinisacked = 1;		} else {			sbdrop(&so->so_snd, acked);			tp->snd_wnd -= acked;			ourfinisacked = 0;		}		sowwakeup(so);		tp->snd_una = th->th_ack;		if (SEQ_LT(tp->snd_nxt, tp->snd_una))			tp->snd_nxt = tp->snd_una;		switch (tp->t_state) {		/*		 * In FIN_WAIT_1 STATE in addition to the processing		 * for the ESTABLISHED state if our FIN is now acknowledged		 * then enter FIN_WAIT_2.		 */		case TCPS_FIN_WAIT_1:			if (ourfinisacked) {				/*				 * If we can't receive any more				 * data, then closing user can proceed.				 * Starting the timer is contrary to the				 * specification, but if we don't get a FIN				 * we'll hang forever.				 */				if (so->so_state & SS_CANTRCVMORE) {					soisdisconnected(so);					callout_reset(tp->tt_2msl, tcp_maxidle,						      tcp_timer_2msl, tp);				}				tp->t_state = TCPS_FIN_WAIT_2;			}			break;	 	/*		 * In CLOSING STATE in addition to the processing for		 * the ESTABLISHED state if the ACK acknowledges our FIN		 * then enter the TIME-WAIT state, otherwise ignore		 * the segment.		 */		case TCPS_CLOSING:			if (ourfinisacked) {				tp->t_state = TCPS_TIME_WAIT;				tcp_canceltimers(tp);				/* Shorten TIME_WAIT [RFC-1644, p.28] */				if (tp->cc_recv != 0 &&				    (ticks - tp->t_starttime) < tcp_msl) {					callout_reset(tp->tt_2msl,						      tp->t_rxtcur *						      TCPTV_TWTRUNC,						      tcp_timer_2msl, tp);                                }				else {					callout_reset(tp->tt_2msl, 2 * tcp_msl,						      tcp_timer_2msl, tp);                                }				soisdisconnected(so);			}			break;		/*		 * In LAST_ACK, we may still be waiting for data to drain		 * and/or to be acked, as well as for the ack of our FIN.		 * If our FIN is now acknowledged, delete the TCB,		 * enter the closed state and return.		 */		case TCPS_LAST_ACK:			if (ourfinisacked) {				tp = tcp_close(tp);				goto drop;			}			break;		/*		 * In TIME_WAIT state the only thing that should arrive		 * is a retransmission of the remote FIN.  Acknowledge		 * it and restart the finack timer.		 */		case TCPS_TIME_WAIT:			callout_reset(tp->tt_2msl, 2 * tcp_msl,				      tcp_timer_2msl, tp);			goto dropafterack;		}	}step6:	/*	 * Update window information.	 * Don't look at window if no ACK: TAC's send garbage on first SYN.	 */	if ((thflags & TH_ACK) &&	    (SEQ_LT(tp->snd_wl1, th->th_seq) ||	    (tp->snd_wl1 == th->th_seq && (SEQ_LT(tp->snd_wl2, th->th_ack) ||	     (tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd))))) {		/* keep track of pure window updates */		if (tlen == 0 &&		    tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd)			tcpstat.tcps_rcvwinupd++;		tp->snd_wnd = tiwin;		tp->snd_wl1 = th->th_seq;		tp->snd_wl2 = th->th_ack;		if (tp->snd_wnd > tp->max_sndwnd)			tp->max_sndwnd = tp->snd_wnd;		needoutput = 1;	}	/*	 * Process segments with URG.	 */	if ((thflags & TH_URG) && th->th_urp &&	    TCPS_HAVERCVDFIN(tp->t_state) == 0) {		/*		 * This is a kludge, but if we receive and accept		 * random urgent pointers, we'll crash in		 * soreceive.  It's hard to imagine someone		 * actually wanting to send this much urgent data.		 */		if (th->th_urp + so->so_rcv.sb_cc > sb_max) {			th->th_urp = 0;			/* XXX */			thflags &= ~TH_URG;		/* XXX */			goto dodata;			/* XXX */		}		/*		 * If this segment advances the known urgent pointer,		 * then mark the data stream.  This should not happen		 * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since		 * a FIN has been received from the remote side.		 * In these states we ignore the URG.		 *		 * According to RFC961 (Assigned Protocols),		 * the urgent pointer points to the last octet		 * of urgent data.  We continue, however,		 * to consider it to indicate the first octet		 * of data past the urgent section as the original		 * spec states (in one of two places).		 */		if (SEQ_GT(th->th_seq+th->th_urp, tp->rcv_up)) {			tp->rcv_up = th->th_seq + th->th_urp;			so->so_oobmark = so->so_rcv.sb_cc +			    (tp->rcv_up - tp->rcv_nxt) - 1;			if (so->so_oobmark == 0)				so->so_state |= SS_RCVATMARK;			sohasoutofband(so);			tp->t_oobflags &= ~(TCPOOB_HAVEDATA | TCPOOB_HADDATA);		}		/*		 * Remove out of band data so doesn't get presented to user.		 * This can happen independent of advancing the URG pointer,		 * but if two URG's are pending at once, some out-of-band		 * data may creep in... ick.		 */		if (th->th_urp <= (u_long)tlen#ifdef SO_OOBINLINE		     && (so->so_options & SO_OOBINLINE) == 0#endif		     )			tcp_pulloutofband(so, th, m,				drop_hdrlen);	/* hdr drop is delayed */	} else		/*		 * If no out of band data is expected,		 * pull receive urgent pointer along		 * with the receive window.		 */		if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))			tp->rcv_up = tp->rcv_nxt;dodata:							/* XXX */	/*	 * Process the segment text, merging it into the TCP sequencing queue,	 * and arranging for acknowledgment of receipt if necessary.	 * This process logically involves adjusting tp->rcv_wnd as data	 * is presented to the user (this happens in tcp_usrreq.c,	 * case PRU_RCVD).  If a FIN has already been received on this	 * connection then we just ignore the text.	 */	if ((tlen || (thflags&TH_FIN)) &&	    TCPS_HAVERCVDFIN(tp->t_state) == 0) {		m_adj(m, drop_hdrlen);	/* delayed header drop */		/*		 * Insert segment which inludes th into reassembly queue of tcp with		 * control block tp.  Return TH_FIN if reassembly now includes		 * a segment with FIN.  This handle the common case inline (segment		 * is the next to be received on an established connection, and the		 * queue is empty), avoiding linkage into and removal from the queue		 * and repetition of various conversions.		 * Set DELACK for segments received in order, but ack immediately		 * when segments are out of order (so fast retransmit can work).		 */		if (th->th_seq == tp->rcv_nxt &&		    LIST_EMPTY(&tp->t_segq) &&		    TCPS_HAVEESTABLISHED(tp->t_state)) {			if (DELAY_ACK(tp))				callout_reset(tp->tt_delack, tcp_delacktime,				    tcp_timer_delack, tp);			else				tp->t_flags |= TF_ACKNOW;			tp->rcv_nxt += tlen;			thflags = th->th_flags & TH_FIN;			tcpstat.tcps_rcvpack++;			tcpstat.tcps_rcvbyte += tlen;			ND6_HINT(tp);			sbappend(&so->so_rcv, m);			sorwakeup(so);		} else {			thflags = tcp_reass(tp, th, &tlen, m);			tp->t_flags |= TF_ACKNOW;		}		/*		 * Note the amount of data that peer has sent into		 * our window, in order to estimate the sender's		 * buffer size.		 */		len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt);	} else {		m_freem(m);		thflags &= ~TH_FIN;	}	/*	 * If FIN is received ACK the FIN and let the user know	 * that the connection is closing.	 */	if (thflags & TH_FIN) {		if (TCPS_HAVERCVDFIN(tp->t_state) == 0) {			socantrcvmore(so);			/*			 *  If connection is half-synchronized			 *  (ie NEEDSYN flag on) then delay ACK,			 *  so it may be piggybacked whe

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?