📄 tcp_input.c
字号:
* are above the previous ones.
*/
if (tiflags & TH_SYN &&
tp->t_state == TCPS_TIME_WAIT &&
SEQ_GT(th->th_seq, tp->rcv_nxt)) {
iss = tp->snd_nxt + TCP_ISSINCR;
tp = tcp_close(tp);
goto findpcb;
}
/*
* If window is closed can only take segments at
* window edge, and have to drop data and PUSH from
* incoming segments. Continue processing, but
* remember to ack. Otherwise, drop segment
* and ack.
*/
if (tp->rcv_wnd == 0 && th->th_seq == tp->rcv_nxt) {
tp->t_flags |= TF_ACKNOW;
tcpstat.tcps_rcvwinprobe++;
} else
goto dropafterack;
} else
tcpstat.tcps_rcvbyteafterwin += todrop;
m_adj(m, -todrop);
tlen -= todrop;
tiflags &= ~(TH_PUSH|TH_FIN);
}
/*
* If last ACK falls within this segment's sequence numbers,
* record its timestamp.
* Fix from Braden, see Stevens p. 870
*/
if (ts_present && TSTMP_GEQ(ts_val, tp->ts_recent) &&
SEQ_LEQ(th->th_seq, tp->last_ack_sent)) {
tp->ts_recent_age = tcp_now;
tp->ts_recent = ts_val;
}
/*
* If the RST bit is set examine the state:
* SYN_RECEIVED STATE:
* If passive open, return to LISTEN state.
* If active open, inform user that connection was refused.
* ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
* Inform user that connection was reset, and close tcb.
* CLOSING, LAST_ACK, TIME_WAIT STATES
* Close the tcb.
*/
if (tiflags & TH_RST) {
#ifndef INET6
if (ti->ti_seq != tp->last_ack_sent)
#else
if (th->th_seq != tp->last_ack_sent)
#endif
goto drop;
switch (tp->t_state) {
case TCPS_SYN_RECEIVED:
so->so_error = ECONNREFUSED;
goto close;
case TCPS_ESTABLISHED:
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
case TCPS_CLOSE_WAIT:
so->so_error = ECONNRESET;
close:
tp->t_state = TCPS_CLOSED;
tcpstat.tcps_drops++;
tp = tcp_close(tp);
goto drop;
case TCPS_CLOSING:
case TCPS_LAST_ACK:
case TCPS_TIME_WAIT:
tp = tcp_close(tp);
goto drop;
}
}
/*
* If a SYN is in the window, then this is an
* error and we send an RST and drop the connection.
*/
if (tiflags & TH_SYN) {
tp = tcp_drop(tp, ECONNRESET);
goto dropwithreset;
}
/*
* If the ACK bit is off we drop the segment and return.
*/
if ((tiflags & TH_ACK) == 0) {
if (tp->t_flags & TF_ACKNOW)
goto dropafterack;
else
goto drop;
}
/*
* Ack processing.
*/
switch (tp->t_state) {
/*
* In SYN_RECEIVED state, the ack ACKs our SYN, so enter
* ESTABLISHED state and continue processing.
* The ACK was checked above.
*/
case TCPS_SYN_RECEIVED:
tcpstat.tcps_connects++;
soisconnected(so);
tp->t_state = TCPS_ESTABLISHED;
/* 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;
}
(void) tcp_reass(tp, (struct tcphdr *)0, (struct mbuf *)0,
&tlen);
tp->snd_wl1 = th->th_seq - 1;
/* fall into ... */
/*
* In ESTABLISHED state: drop duplicate ACKs; ACK out of range
* ACKs. If the ack is in the range
* tp->snd_una < ti->ti_ack <= tp->snd_max
* then advance tp->snd_una to ti->ti_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)) {
/*
* Duplicate/old ACK processing.
* Increments t_dupacks:
* Pure duplicate (same seq/ack/window, no data)
* Doesn't affect t_dupacks:
* Data packets.
* Normal window updates (window opens)
* Resets t_dupacks:
* New data ACKed.
* Window shrinks
* Old ACK
*/
if (tlen)
break;
/*
* If we get an old ACK, there is probably packet
* reordering going on. Be conservative and reset
* t_dupacks so that we are less agressive in
* doing a fast retransmit.
*/
if (th->th_ack != tp->snd_una) {
tp->t_dupacks = 0;
break;
}
if (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 (tp->t_timer[TCPT_REXMT] == 0)
tp->t_dupacks = 0;
#if defined(TCP_SACK) && defined(TCP_FACK)
/*
* In FACK, can enter fast rec. if the receiver
* reports a reass. queue longer than 3 segs.
*/
else if (++tp->t_dupacks == tcprexmtthresh ||
((SEQ_GT(tp->snd_fack, tcprexmtthresh *
tp->t_maxseg + tp->snd_una)) &&
SEQ_GT(tp->snd_una, tp->snd_last))) {
#else
else if (++tp->t_dupacks == tcprexmtthresh) {
#endif /* TCP_FACK */
tcp_seq onxt = tp->snd_nxt;
u_long win =
ulmin(tp->snd_wnd, tp->snd_cwnd) /
2 / tp->t_maxseg;
#if defined(TCP_SACK) || defined(TCP_NEWRENO)
if (SEQ_LT(th->th_ack, tp->snd_last)){
/*
* False fast retx after
* timeout. Do not cut window.
*/
tp->snd_cwnd += tp->t_maxseg;
tp->t_dupacks = 0;
(void) tcp_output(tp);
goto drop;
}
#endif
if (win < 2)
win = 2;
tp->snd_ssthresh = win * tp->t_maxseg;
#if defined(TCP_SACK) || defined(TCP_NEWRENO)
tp->snd_last = tp->snd_max;
#endif
#ifdef TCP_SACK
if (!tp->sack_disable) {
tp->t_timer[TCPT_REXMT] = 0;
tp->t_rtt = 0;
tcpstat.tcps_sndrexmitfast++;
#if defined(TCP_SACK) && defined(TCP_FACK)
(void) tcp_output(tp);
/*
* During FR, snd_cwnd is held
* constant for FACK.
*/
tp->snd_cwnd = tp->snd_ssthresh;
tp->t_dupacks = tcprexmtthresh;
#else
/*
* tcp_output() will send
* oldest SACK-eligible rtx.
*/
(void) tcp_output(tp);
tp->snd_cwnd = tp->snd_ssthresh+
tp->t_maxseg * tp->t_dupacks;
#endif /* TCP_FACK */
goto drop;
}
#endif /* TCP_SACK */
tp->t_timer[TCPT_REXMT] = 0;
tp->t_rtt = 0;
tp->snd_nxt = th->th_ack;
tp->snd_cwnd = tp->t_maxseg;
tcpstat.tcps_sndrexmitfast++;
(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) {
#if defined(TCP_SACK) && defined(TCP_FACK)
/*
* while (awnd < cwnd)
* sendsomething();
*/
if (!tp->sack_disable) {
if (tp->snd_awnd < tp->snd_cwnd)
tcp_output(tp);
goto drop;
}
#endif /* TCP_FACK */
tp->snd_cwnd += tp->t_maxseg;
(void) tcp_output(tp);
goto drop;
}
} else if (tiwin < tp->snd_wnd) {
/*
* The window was retracted! Previous dup
* ACKs may have been due to packets arriving
* after the shrunken window, not a missing
* packet, so play it safe and reset t_dupacks
*/
tp->t_dupacks = 0;
}
break;
}
/*
* If the congestion window was inflated to account
* for the other side's cached packets, retract it.
*/
#ifdef TCP_NEWRENO
if (tp->t_dupacks >= tcprexmtthresh && !tcp_newreno(tp, th)) {
/* Out of fast recovery */
tp->snd_cwnd = tp->snd_ssthresh;
/*
* 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 (tcp_seq_subtract(tp->snd_max, th->th_ack) <
tp->snd_ssthresh)
tp->snd_cwnd = tcp_seq_subtract(tp->snd_max,
th->th_ack) + tp->t_maxseg;
tp->t_dupacks = 0;
}
#elif defined(TCP_SACK)
if (!tp->sack_disable) {
if (tp->t_dupacks >= tcprexmtthresh) {
/* Check for a partial ACK */
if (tcp_sack_partialack(tp, th)) {
#if defined(TCP_SACK) && defined(TCP_FACK)
/* Force call to tcp_output */
if (tp->snd_awnd < tp->snd_cwnd)
needoutput = 1;
#else
tp->snd_cwnd += tp->t_maxseg;
needoutput = 1;
#endif /* TCP_FACK */
} else {
/* Out of fast recovery */
tp->snd_cwnd = tp->snd_ssthresh;
if (tcp_seq_subtract(tp->snd_max,
th->th_ack) < tp->snd_ssthresh)
tp->snd_cwnd =
tcp_seq_subtract(tp->snd_max,
th->th_ack) + tp->t_maxseg;
tp->t_dupacks = 0;
#if defined(TCP_SACK) && defined(TCP_FACK)
if (SEQ_GT(th->th_ack, tp->snd_fack))
tp->snd_fack = th->th_ack;
#endif /* TCP_FACK */
}
}
} else {
if (tp->t_dupacks >= tcprexmtthresh &&
!tcp_newreno(tp, th)) {
/* Out of fast recovery */
tp->snd_cwnd = tp->snd_ssthresh;
if (tcp_seq_subtract(tp->snd_max, th->th_ack) <
tp->snd_ssthresh)
tp->snd_cwnd =
tcp_seq_subtract(tp->snd_max,
th->th_ack) + tp->t_maxseg;
tp->t_dupacks = 0;
}
}
#else /* else neither TCP_NEWRENO nor TCP_SACK */
if (tp->t_dupacks >= tcprexmtthresh &&
tp->snd_cwnd > tp->snd_ssthresh)
tp->snd_cwnd = tp->snd_ssthresh;
tp->t_dupacks = 0;
#endif
if (SEQ_GT(th->th_ack, tp->snd_max)) {
tcpstat.tcps_rcvacktoomuch++;
goto dropafterack;
}
acked = th->th_ack - tp->snd_una;
tcpstat.tcps_rcvackpack++;
tcpstat.tcps_rcvackbyte += acked;
/*
* 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 (ts_present)
tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
else if (tp->t_rtt && SEQ_GT(th->th_ack, tp->t_rtseq))
tcp_xmit_timer(tp,tp->t_rtt);
/*
* 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) {
tp->t_timer[TCPT_REXMT] = 0;
needoutput = 1;
} else if (tp->t_timer[TCPT_PERSIST] == 0)
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
/*
* 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 defined (TCP_NEWRENO) || defined (TCP_SACK)
if (SEQ_GEQ(th->th_ack, tp->snd_last))
#endif
tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale);
}
ND6_HINT(tp);
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;
}
if (sb_notify(&so->so_snd))
sowwakeup(so);
tp->snd_una = th->th_ack;
if (SEQ_LT(tp->snd_nxt, tp->snd_una))
tp->snd_nxt = tp->snd_una;
#if defined (TCP_SACK) && defined (TCP_FACK)
if (SEQ_GT(tp->snd_una, tp->snd_fack))
tp->snd_fack = tp->snd_una;
#endif
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);
tp->t_timer[TCPT_2MSL] = tcp_maxidle;
}
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.
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -