📄 tcp_input.c
字号:
* SYN_SENT* --> FIN_WAIT_1
*/
if (tp->t_flags & TF_NEEDFIN) {
tp->t_state = TCPS_FIN_WAIT_1;
tp->t_flags &= ~TF_NEEDFIN;
tiflags &= ~TH_SYN;
} else {
tp->t_state = TCPS_ESTABLISHED;
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
}
} else {
/*
* Received initial SYN in SYN-SENT[*] state => simul-
* taneous open. If segment contains CC option and there is
* a cached CC, apply TAO test; if it succeeds, connection is
* half-synchronized. Otherwise, do 3-way handshake:
* SYN-SENT -> SYN-RECEIVED
* SYN-SENT* -> SYN-RECEIVED*
* If there was no CC option, clear cached CC value.
*/
tp->t_flags |= TF_ACKNOW;
tp->t_timer[TCPT_REXMT] = 0;
if (to.to_flag & TOF_CC) {
if (taop->tao_cc != 0 &&
CC_GT(to.to_cc, taop->tao_cc)) {
/*
* update cache and make transition:
* SYN-SENT -> ESTABLISHED*
* SYN-SENT* -> FIN-WAIT-1*
*/
taop->tao_cc = to.to_cc;
if (tp->t_flags & TF_NEEDFIN) {
tp->t_state = TCPS_FIN_WAIT_1;
tp->t_flags &= ~TF_NEEDFIN;
} else {
tp->t_state = TCPS_ESTABLISHED;
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
}
tp->t_flags |= TF_NEEDSYN;
} else
tp->t_state = TCPS_SYN_RECEIVED;
} else {
/* CC.NEW or no option => invalidate cache */
taop->tao_cc = 0;
tp->t_state = TCPS_SYN_RECEIVED;
}
}
trimthenstep6:
/*
* Advance ti->ti_seq to correspond to first data byte.
* If data, trim to stay within window,
* dropping FIN if necessary.
*/
ti->ti_seq++;
if (ti->ti_len > tp->rcv_wnd) {
todrop = ti->ti_len - tp->rcv_wnd;
m_adj(m, -todrop);
ti->ti_len = tp->rcv_wnd;
tiflags &= ~TH_FIN;
tcpstat.tcps_rcvpackafterwin++;
tcpstat.tcps_rcvbyteafterwin += todrop;
}
tp->snd_wl1 = ti->ti_seq - 1;
tp->rcv_up = ti->ti_seq;
/*
* Client side of transaction: already sent SYN and data.
* If the remote host used T/TCP to validate the SYN,
* our data will be ACK'd; if so, enter normal data segment
* processing in the middle of step 5, ack processing.
* Otherwise, goto step 6.
*/
if (tiflags & TH_ACK)
goto process_ACK;
goto step6;
/*
* If the state is LAST_ACK or CLOSING or TIME_WAIT:
* if segment contains a SYN and CC [not CC.NEW] option:
* if state == TIME_WAIT and connection duration > MSL,
* drop packet and send RST;
*
* if SEG.CC > CCrecv then is new SYN, and can implicitly
* ack the FIN (and data) in retransmission queue.
* Complete close and delete TCPCB. Then reprocess
* segment, hoping to find new TCPCB in LISTEN state;
*
* else must be old SYN; drop it.
* else do normal processing.
*/
case TCPS_LAST_ACK:
case TCPS_CLOSING:
case TCPS_TIME_WAIT:
if ((tiflags & TH_SYN) &&
(to.to_flag & TOF_CC) && tp->cc_recv != 0) {
if (tp->t_state == TCPS_TIME_WAIT &&
tp->t_duration > TCPTV_MSL)
goto dropwithreset;
if (CC_GT(to.to_cc, tp->cc_recv)) {
tp = tcp_close(tp);
goto findpcb;
}
else
goto drop;
}
break; /* continue normal processing */
}
/*
* States other than LISTEN or SYN_SENT.
* First check timestamp, if present.
* Then check the connection count, if present.
* Then check that at least some bytes of segment are within
* receive window. If segment begins before rcv_nxt,
* drop leading data (and SYN); if nothing left, just ack.
*
* RFC 1323 PAWS: If we have a timestamp reply on this segment
* and it's less than ts_recent, drop it.
*/
if ((to.to_flag & TOF_TS) != 0 && (tiflags & TH_RST) == 0 &&
tp->ts_recent && TSTMP_LT(to.to_tsval, tp->ts_recent)) {
/* Check to see if ts_recent is over 24 days old. */
if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
/*
* Invalidate ts_recent. If this segment updates
* ts_recent, the age will be reset later and ts_recent
* will get a valid value. If it does not, setting
* ts_recent to zero will at least satisfy the
* requirement that zero be placed in the timestamp
* echo reply when ts_recent isn't valid. The
* age isn't reset until we get a valid ts_recent
* because we don't want out-of-order segments to be
* dropped when ts_recent is old.
*/
tp->ts_recent = 0;
} else {
tcpstat.tcps_rcvduppack++;
tcpstat.tcps_rcvdupbyte += ti->ti_len;
tcpstat.tcps_pawsdrop++;
goto dropafterack;
}
}
/*
* T/TCP mechanism
* If T/TCP was negotiated and the segment doesn't have CC,
* or if it's CC is wrong then drop the segment.
* RST segments do not have to comply with this.
*/
if ((tp->t_flags & (TF_REQ_CC|TF_RCVD_CC)) == (TF_REQ_CC|TF_RCVD_CC) &&
((to.to_flag & TOF_CC) == 0 || tp->cc_recv != to.to_cc) &&
(tiflags & TH_RST) == 0)
goto dropafterack;
todrop = tp->rcv_nxt - ti->ti_seq;
if (todrop > 0) {
if (tiflags & TH_SYN) {
tiflags &= ~TH_SYN;
ti->ti_seq++;
if (ti->ti_urp > 1)
ti->ti_urp--;
else
tiflags &= ~TH_URG;
todrop--;
}
/*
* Following if statement from Stevens, vol. 2, p. 960.
*/
if (todrop > ti->ti_len
|| (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) {
/*
* Any valid FIN must be to the left of the window.
* At this point the FIN must be a duplicate or out
* of sequence; drop it.
*/
tiflags &= ~TH_FIN;
/*
* Send an ACK to resynchronize and drop any data.
* But keep on processing for RST or ACK.
*/
tp->t_flags |= TF_ACKNOW;
todrop = ti->ti_len;
tcpstat.tcps_rcvduppack++;
tcpstat.tcps_rcvdupbyte += todrop;
} else {
tcpstat.tcps_rcvpartduppack++;
tcpstat.tcps_rcvpartdupbyte += todrop;
}
m_adj(m, todrop);
ti->ti_seq += todrop;
ti->ti_len -= todrop;
if (ti->ti_urp > todrop)
ti->ti_urp -= todrop;
else {
tiflags &= ~TH_URG;
ti->ti_urp = 0;
}
}
/*
* If new data are received on a connection after the
* user processes are gone, then RST the other end.
*/
if ((so->so_state & SS_NOFDREF) &&
tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) {
tp = tcp_close(tp);
tcpstat.tcps_rcvafterclose++;
goto dropwithreset;
}
/*
* If segment ends after window, drop trailing data
* (and PUSH and FIN); if nothing left, just ACK.
*/
todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd);
if (todrop > 0) {
tcpstat.tcps_rcvpackafterwin++;
if (todrop >= ti->ti_len) {
tcpstat.tcps_rcvbyteafterwin += ti->ti_len;
/*
* If a new connection request is received
* while in TIME_WAIT, drop the old connection
* and start over if the sequence numbers
* are above the previous ones.
*/
if (tiflags & TH_SYN &&
tp->t_state == TCPS_TIME_WAIT &&
SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
iss = tp->rcv_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 && ti->ti_seq == tp->rcv_nxt) {
tp->t_flags |= TF_ACKNOW;
tcpstat.tcps_rcvwinprobe++;
} else
goto dropafterack;
} else
tcpstat.tcps_rcvbyteafterwin += todrop;
m_adj(m, -todrop);
ti->ti_len -= todrop;
tiflags &= ~(TH_PUSH|TH_FIN);
}
/*
* If last ACK falls within this segment's sequence numbers,
* record its timestamp.
* NOTE that the test is modified according to the latest
* proposal of the tcplw@cray.com list (Braden 1993/04/26).
*/
if ((to.to_flag & TOF_TS) != 0 &&
SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) {
tp->ts_recent_age = tcp_now;
tp->ts_recent = to.to_tsval;
}
/*
* 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) 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: if in SYN-RECEIVED state or SENDSYN
* flag is on (half-synchronized state), then queue data for
* later processing; else drop segment and return.
*/
if ((tiflags & TH_ACK) == 0) {
if (tp->t_state == TCPS_SYN_RECEIVED ||
(tp->t_flags & TF_NEEDSYN))
goto step6;
else
goto drop;
}
/*
* Ack processing.
*/
switch (tp->t_state) {
/*
* In SYN_RECEIVED state if the ack ACKs our SYN then enter
* ESTABLISHED state and continue processing, otherwise
* send an RST.
*/
case TCPS_SYN_RECEIVED:
if (SEQ_GT(tp->snd_una, ti->ti_ack) ||
SEQ_GT(ti->ti_ack, tp->snd_max))
goto dropwithreset;
tcpstat.tcps_connects++;
soisconnected(so);
/* 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;
}
/*
* Upon successful completion of 3-way handshake,
* update cache.CC if it was undefined, pass any queued
* data to the user, and advance state appropriately.
*/
if ((taop = tcp_gettaocache(inp)) != NULL &&
taop->tao_cc == 0)
taop->tao_cc = tp->cc_recv;
/*
* Make transitions:
* SYN-RECEIVED -> ESTABLISHED
* SYN-RECEIVED* -> FIN-WAIT-1
*/
if (tp->t_flags & TF_NEEDFIN) {
tp->t_state = TCPS_FIN_WAIT_1;
tp->t_flags &= ~TF_NEEDFIN;
} else {
tp->t_state = TCPS_ESTABLISHED;
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
}
/*
* If segment contains data or ACK, will call tcp_reass()
* later; if not, do so now to pass queued data to user.
*/
if (ti->ti_len == 0 && (tiflags & TH_FIN) == 0)
(void) tcp_reass(tp, (struct tcpiphdr *)0,
(struct mbuf *)0);
tp->snd_wl1 = ti->ti_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(ti->ti_ack, tp->snd_una)) {
if (ti->ti_len == 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 (tp->t_timer[TCPT_REXMT] == 0 ||
ti->ti_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 (win < 2)
win = 2;
tp->snd_ssthresh = win * tp->t_maxseg;
tp->t_timer[TCPT_REXMT] = 0;
tp->t_rtt = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -