📄 tcp_input.c
字号:
tp->t_flags &= ~TF_RCVD_CC;
tcpstat.tcps_connects++;
soisconnected(so);
/* Do window scaling on this connection? */
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;
}
/* Segment is acceptable, update cache if undefined. */
if (taop->tao_ccsent == 0)
taop->tao_ccsent = to.to_ccecho;
tp->rcv_adv += tp->rcv_wnd;
tp->snd_una++; /* SYN is acked */
/*
* If there's data, delay ACK; if there's also a FIN
* ACKNOW will be turned on later.
*/
if (DELAY_ACK(tp) && tlen != 0)
callout_reset(tp->tt_delack, tcp_delacktime,
tcp_timer_delack, tp);
else
tp->t_flags |= TF_ACKNOW;
/*
* Received <SYN,ACK> in SYN_SENT[*] state.
* Transitions:
* SYN_SENT --> ESTABLISHED
* SYN_SENT* --> FIN_WAIT_1
*/
tp->t_starttime = ticks;
if (tp->t_flags & TF_NEEDFIN) {
tp->t_state = TCPS_FIN_WAIT_1;
tp->t_flags &= ~TF_NEEDFIN;
thflags &= ~TH_SYN;
} else {
tp->t_state = TCPS_ESTABLISHED;
callout_reset(tp->tt_keep, tcp_keepidle,
tcp_timer_keep, tp);
}
} 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;
callout_stop(tp->tt_rexmt);
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;
tp->t_starttime = ticks;
if (tp->t_flags & TF_NEEDFIN) {
tp->t_state = TCPS_FIN_WAIT_1;
tp->t_flags &= ~TF_NEEDFIN;
} else {
tp->t_state = TCPS_ESTABLISHED;
callout_reset(tp->tt_keep,
tcp_keepidle,
tcp_timer_keep,
tp);
}
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 th->th_seq to correspond to first data byte.
* If data, trim to stay within window,
* dropping FIN if necessary.
*/
th->th_seq++;
if (tlen > tp->rcv_wnd) {
todrop = tlen - tp->rcv_wnd;
m_adj(m, -todrop);
tlen = tp->rcv_wnd;
thflags &= ~TH_FIN;
tcpstat.tcps_rcvpackafterwin++;
tcpstat.tcps_rcvbyteafterwin += todrop;
}
tp->snd_wl1 = th->th_seq - 1;
tp->rcv_up = th->th_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 (thflags & 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 ((thflags & TH_SYN) &&
(to.to_flag & TOF_CC) && tp->cc_recv != 0) {
if (tp->t_state == TCPS_TIME_WAIT &&
(ticks - tp->t_starttime) > tcp_msl) {
rstreason = BANDLIM_UNLIMITED;
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 the RST flag and sequence number since reset segments
* are exempt from the timestamp and connection count tests. This
* fixes a bug introduced by the Stevens, vol. 2, p. 960 bugfix
* below which allowed reset segments in half the sequence space
* to fall though and be processed (which gives forged reset
* segments with a random sequence number a 50 percent chance of
* killing a connection).
* Then 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.
*
*
* If the RST bit is set, check the sequence number to see
* if this is a valid reset segment.
* RFC 793 page 37:
* In all states except SYN-SENT, all reset (RST) segments
* are validated by checking their SEQ-fields. A reset is
* valid if its sequence number is in the window.
* Note: this does not take into account delayed ACKs, so
* we should test against last_ack_sent instead of rcv_nxt.
* The sequence number in the reset segment is normally an
* echo of our outgoing acknowlegement numbers, but some hosts
* send a reset with the sequence number at the rightmost edge
* of our receive window, and we have to handle this case.
* If we have multiple segments in flight, the intial reset
* segment sequence numbers will be to the left of last_ack_sent,
* but they will eventually catch up.
* In any case, it never made sense to trim reset segments to
* fit the receive window since RFC 1122 says:
* 4.2.2.12 RST Segment: RFC-793 Section 3.4
*
* A TCP SHOULD allow a received RST segment to include data.
*
* DISCUSSION
* It has been suggested that a RST segment could contain
* ASCII text that encoded and explained the cause of the
* RST. No standard has yet been established for such
* data.
*
* If the reset segment passes the sequence number test 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 STATES:
* Close the tcb.
* TIME_WAIT STATE:
* Drop the segment - see Stevens, vol. 2, p. 964 and
* RFC 1337.
*/
if (thflags & TH_RST) {
if (SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) {
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);
break;
case TCPS_CLOSING:
case TCPS_LAST_ACK:
tp = tcp_close(tp);
break;
case TCPS_TIME_WAIT:
break;
}
}
goto drop;
}
/*
* 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 && tp->ts_recent &&
TSTMP_LT(to.to_tsval, tp->ts_recent)) {
/* Check to see if ts_recent is over 24 days old. */
if ((int)(ticks - 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 += tlen;
tcpstat.tcps_pawsdrop++;
goto dropafterack;
}
}
/*
* T/TCP mechanism
* If T/TCP was negotiated and the segment doesn't have CC,
* or if its 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))
goto dropafterack;
/*
* In the SYN-RECEIVED state, validate that the packet belongs to
* this connection before trimming the data to fit the receive
* window. Check the sequence number versus IRS since we know
* the sequence numbers haven't wrapped. This is a partial fix
* for the "LAND" DoS attack.
*/
if (tp->t_state == TCPS_SYN_RECEIVED && SEQ_LT(th->th_seq, tp->irs)) {
rstreason = BANDLIM_UNLIMITED;
goto dropwithreset;
}
todrop = tp->rcv_nxt - th->th_seq;
if (todrop > 0) {
if (thflags & TH_SYN) {
thflags &= ~TH_SYN;
th->th_seq++;
if (th->th_urp > 1)
th->th_urp--;
else
thflags &= ~TH_URG;
todrop--;
}
/*
* Following if statement from Stevens, vol. 2, p. 960.
*/
if (todrop > tlen
|| (todrop == tlen && (thflags & 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.
*/
thflags &= ~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 = tlen;
tcpstat.tcps_rcvduppack++;
tcpstat.tcps_rcvdupbyte += todrop;
} else {
tcpstat.tcps_rcvpartduppack++;
tcpstat.tcps_rcvpartdupbyte += todrop;
}
drop_hdrlen += todrop; /* drop from the top afterwards */
th->th_seq += todrop;
tlen -= todrop;
if (th->th_urp > todrop)
th->th_urp -= todrop;
else {
thflags &= ~TH_URG;
th->th_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 && tlen) {
tp = tcp_close(tp);
tcpstat.tcps_rcvafterclose++;
rstreason = BANDLIM_UNLIMITED;
goto dropwithreset;
}
/*
* If segment ends after window, drop trailing data
* (and PUSH and FIN); if nothing left, just ACK.
*/
todrop = (th->th_seq+tlen) - (tp->rcv_nxt+tp->rcv_wnd);
if (todrop > 0) {
tcpstat.tcps_rcvpackafterwin++;
if (todrop >= tlen) {
tcpstat.tcps_rcvbyteafterwin += tlen;
/*
* 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 (thflags & TH_SYN &&
tp->t_state == TCPS_TIME_WAIT &&
SEQ_GT(th->th_seq, tp->rcv_nxt)) {
iss = tcp_new_isn(tp);
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;
thflags &= ~(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(th->th_seq, tp->last_ack_sent)) {
tp->ts_recent_age = ticks;
tp->ts_recent = to.to_tsval;
}
/*
* If a SYN is in the window, then this is an
* error and we send an RST and drop the connection.
*/
if (thflags & TH_SYN) {
tp = tcp_drop(tp, ECONNRESET);
rstreason = BANDLIM_UNLIMITED;
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 ((thflags & 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, 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);
/* 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -