📄 tcpin.c
字号:
/*
* Segment received on connection.
* Reset idle time and keep-alive timer.
*/
tp->t_idle = 0;
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
/*
* Process options if not in LISTEN state,
* else do it below (after getting remote address).
*/
if (opts && (tp->t_state != TCPS_LISTEN) )
{
m_tcpoptions(tp, opts, optlen);
opts = NULL;
}
/*
* Calculate amount of space in receive window,
* and then do TCP input processing.
* Receive window is amount of space in rcv queue,
* but not less than advertised window.
*/
{ int win;
win = mt_defrxwin - so->rcvdq.sb_cc;
if (win < 0)
win = 0;
tp->rcv_wnd = (u_short)MAX(win, (int)(tp->rcv_adv - tp->rcv_nxt));
}
#if DebugTcpin <= 0x03
Printu("tcp2\t");
#endif
switch (tp->t_state)
{
/*
* If the state is LISTEN then ignore segment if it contains an RST.
* If the segment contains an ACK then it is bad and send a RST.
* If it does not contain a SYN then it is not interesting; drop it.
* Don't bother responding if the destination was a broadcast.
* Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial
* tp->iss, and send a segment:
* <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>
* Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss.
* Fill in remote peer address fields if not previously specified.
* Enter SYN_RECEIVED state, and process any other fields of this
* segment in this state.
*/
case TCPS_LISTEN:
{
//Printu("TCPS_LISTEN\r\n");
if (tiflags & TH_RST)
GOTO_DROP;
if (tiflags & TH_ACK)
GOTO_DROPWITHRESET;
if ((tiflags & TH_SYN) == 0)
GOTO_DROP;
mask = ~pkt->net->snmask; /* mask for broadcast detection */
if((pip->ip_dest & mask) == mask)
GOTO_DROP;
/* fill in IP connection info */
so->fhost = pip->ip_src;
so->fport = ptcp->th_sport;
so->lhost = pkt->net->n_ipaddr; /* our local address */
so->ifp = pkt->net; /* set interface for conn.*/
m_template(tp); /* set up header template for tcp sends */
if(opts)
m_tcpoptions(tp, opts, optlen);
if (iss)
tp->iss = iss;
else
tp->iss = tcp_iss;
tcp_iss += (unsigned)(TCP_ISSINCR/2);
tp->irs = ptcp->th_seq;
tcp_sendseqinit(tp);
tcp_rcvseqinit(tp);
tp->t_flags |= TF_ACKNOW;
tp->t_state = TCPS_SYN_RECEIVED;
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
dropsocket = 0; /* committed to socket */
TCP_STAT_INC(tcps_accepts);
goto trimthenstep6;
}
/*
* If the state is SYN_SENT:
* if seg contains an ACK, but not for our SYN, drop the input.
* if seg contains a RST, then drop the connection.
* if seg does not contain SYN, then drop it.
* Otherwise this is an acceptable SYN segment
* initialize tp->rcv_nxt and tp->irs
* if seg contais ack then advance tp->snd_una
* if SYN has been acked change to ESTABLISHED else SYN_RCVD state
* arrange for segment to be acked (eventually)
* continue processing rest of data/controls, beginning with URG
*/
case TCPS_SYN_SENT:
#if DebugTcpin <= 0x03
Printu("TCPS_SYN_SENT\r\n");
#endif
so->ifp = pkt->net; /* set interface for conn.*/
if ((tiflags & TH_ACK) &&
(SEQ_LEQ(ptcp->th_ack, tp->iss) ||
SEQ_GT(ptcp->th_ack, tp->snd_max)))
{
GOTO_DROPWITHRESET;
}
if (tiflags & TH_RST)
{
if (tiflags & TH_ACK)
m_tcpdrop(tp, ECONNREFUSED);
GOTO_DROP;
}
if ((tiflags & TH_SYN) == 0)
GOTO_DROP;
if (tiflags & TH_ACK)
{
tp->snd_una = ptcp->th_ack;
if (SEQ_LT(tp->snd_nxt, tp->snd_una))
tp->snd_nxt = tp->snd_una;
}
tp->t_timer[TCPT_REXMT] = 0;
tp->irs = ptcp->th_seq;
tcp_rcvseqinit(tp);
if (so->lhost != pip->ip_dest)
{
/*
* the IP interface may have changed address since we sent our SYN
* (e.g. PPP brings link up as a result of said SYN and gets new
* address via IPCP); if so we need to update the inpcb and the
* TCP header template with the new address.
*/
if (pkt->net->n_ipaddr == pip->ip_dest)
{
so->lhost = pip->ip_dest;
tp->t_template->ti_i.ip_src = pip->ip_dest;
}
}
tp->t_flags |= TF_ACKNOW;
if ((tiflags & TH_ACK) && SEQ_GT(tp->snd_una, tp->iss))
{
TCP_MIB_INC(tcpActiveOpens); /* keep MIB stats */
TCP_STAT_INC(tcps_connects);
tp->t_state = TCPS_ESTABLISHED;
m_connected (so);
tp->t_maxseg = MIN(tp->t_maxseg, TCP_MSS);
/*
* if we didn't have to retransmit the SYN,
* use its rtt as our initial srtt & rtt var.
*/
if (tp->t_rtt)
{
tp->t_srtt = tp->t_rtt << 3;
tp->t_rttvar = tp->t_rtt << 1;
TCPT_RANGESET(tp->t_rxtcur,
(short)(((tp->t_srtt >> 2) + tp->t_rttvar) >> 1),
TCPTV_MIN, TCPTV_REXMTMAX);
tp->t_rtt = 0;
}
} else
tp->t_state = TCPS_SYN_RECEIVED;
trimthenstep6:
/* Advance ptcp->th_seq to correspond to first data byte. */
ptcp->th_seq++;
tp->snd_wl1 = ptcp->th_seq - 1;
tp->rcv_up = ptcp->th_seq;
goto step6;
}
/*
* States other than LISTEN or SYN_SENT.
* First 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.
*/
todrop = (int)(tp->rcv_nxt - ptcp->th_seq);
if (todrop > 0)
{
if (tiflags & TH_SYN)
{
tiflags &= ~TH_SYN;
ptcp->th_seq++;
if (ptcp->th_urp > 1)
ptcp->th_urp--;
else
tiflags &= ~TH_URG;
todrop--;
}
if ((todrop > (int)pkt->m_len) ||
((todrop == (int)pkt->m_len) && ( tiflags&TH_FIN) == 0))
{
TCP_STAT_INC(tcps_rcvduppack);
TCP_STAT_ADD(tcps_rcvdupbyte, pip->ip_len);
/*
* If segment is just one to the left of the window,
* check two special cases:
* 1. Don't toss RST in response to 4.2-style keepalive.
* 2. If the only thing to drop is a FIN, we can drop
* it, but check the ACK or we will get into FIN
* wars if our FINs crossed (both CLOSING).
* In either case, send ACK to resynchronize,
* but keep on processing for RST or ACK.
*/
if ((tiflags & TH_FIN && todrop == (int)pip->ip_len + 1) ||
(tiflags & TH_RST && ptcp->th_seq == tp->rcv_nxt - 1))
{
todrop = pip->ip_len;
tiflags &= ~TH_FIN;
tp->t_flags |= TF_ACKNOW;
}
else
goto dropafterack;
}
else
{
TCP_STAT_INC(tcps_rcvpartduppack);
TCP_STAT_ADD(tcps_rcvpartdupbyte, todrop);
}
#ifdef NPDEBUG
if(todrop > (int)pkt->m_len) /* sanity test */
{
/* dtrap("tcpin 2\n");*/ /* (yaxon del) */
todrop = pkt->m_len;
}
#endif
if(todrop)
{
#if DebugTcpin <= 0x03
Printu("todrop:%d\r\n",todrop);
#endif
pkt->m_len -= todrop; /* trim received packet data */
pkt->m_data += todrop;
ptcp->th_seq += todrop;
pip->ip_len -= (u_short)todrop;
}
if (ptcp->th_urp > (u_short)todrop)
ptcp->th_urp -= (u_short)todrop;
else
{
tiflags &= ~TH_URG;
ptcp->th_urp = 0;
}
}
#ifdef NOTDEF /* this test looks broken in the BSD code too -JB- */
/*
* If new data are received on a connection after the
* user processes are gone, then RST the other end.
*/
if ((tp->t_state > TCPS_CLOSE_WAIT) && pip->ip_len)
{
m_tcpclose(tp);
TCP_STAT_INC(tcps_rcvafterclose);
GOTO_DROPWITHRESET;
}
#endif /* NOTDEF */
/*
* If segment ends after window, drop trailing data
* (and PUSH and FIN); if nothing left, just ACK.
*/
todrop = (int)((ptcp->th_seq + (short)pip->ip_len) - (tp->rcv_nxt + tp->rcv_wnd));
if (todrop > 0)
{
TCP_STAT_INC(tcps_rcvpackafterwin);
if (todrop >= (int)pip->ip_len)
{
TCP_STAT_ADD(tcps_rcvbyteafterwin, pip->ip_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(ptcp->th_seq, tp->rcv_nxt))
{
iss = (tcp_seq)(tp->rcv_nxt + (TCP_ISSINCR));
if (iss & 0xff000000)
{
/* dtrap("tcpin 3\n"); tmp, remove later???? -JB */ /* (yaxon del) */
iss = 0L;
}
m_tcpclose(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 && ptcp->th_seq == tp->rcv_nxt)
{
tp->t_flags |= TF_ACKNOW;
TCP_STAT_INC(tcps_rcvwinprobe);
} else
goto dropafterack;
} else
TCP_STAT_ADD(tcps_rcvbyteafterwin, todrop);
pip->ip_len -= (u_short)todrop; /* trim data from end of packet */
tiflags &= ~(TH_PUSH|TH_FIN);
}
/*
* 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 DebugTcpin <= 0x03
Printu("tcp TH_RSHCHECK\r\n");
#endif
if (tiflags & TH_RST)
switch (tp->t_state)
{
case TCPS_SYN_RECEIVED:
so->error = ECONNREFUSED;
goto close;
case TCPS_ESTABLISHED:
TCP_MIB_INC(tcpEstabResets); /* keep MIB stats */
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
case TCPS_CLOSE_WAIT:
so->error = ECONNRESET;
close:
tp->t_state = TCPS_CLOSED;
TCP_STAT_INC(tcps_drops);
m_tcpclose(tp);
if (so->callback)
so->callback(M_CLOSED, so, NULL);
GOTO_DROP;
case TCPS_CLOSING:
case TCPS_LAST_ACK:
case TCPS_TIME_WAIT:
m_tcpclose(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)
{
m_tcpdrop(tp, ECONNRESET);
GOTO_DROPWITHRESET;
}
/*
* If the ACK bit is off we drop the segment and return.
*/
if ((tiflags & TH_ACK) == 0)
GOTO_DROP;
/*
* Ack processing.
*/
#if DebugTcpin <= 0x03
Printu("tcp ack\r\n");
#endif
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, ptcp->th_ack) ||
SEQ_GT(ptcp->th_ack, tp->snd_max))
{
TCP_MIB_INC(tcpEstabResets); /* keep MIB stats */
GOTO_DROPWITHRESET;
}
TCP_STAT_INC(tcps_connects);
TCP_MIB_INC(tcpPassiveOpens); /* keep MIB stats */
tp->t_state = TCPS_ESTABLISHED;
m_connected(so);
tp->t_maxseg = MIN(tp->t_maxseg, TCP_MSS);
tp->snd_wl1 = ptcp->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 < ptcp->th_ack <= tp->snd_max
* then advance tp->snd_una to ptcp->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(ptcp->th_ack, tp->snd_una))
{
if (pip->ip_len == 0 && ptcp->th_win == tp->snd_wnd)
{
TCP_STAT_INC(tcps_rcvdupack);
/*
* If we have outstanding data (not 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. If this packet fills the
* only hole in the receiver's seq.
* space, the next real ack will fully
* open our window. This means we
* have to do the usual slow-start to
* not overwhelm an intermediate gateway
* with a burst of packets. Leave
* here with the congestion window set
* to allow 2 packets on the next real
* ack and the exp-to-linear thresh
* set for half the current window
* size (since we know we're losing at
* the current window size).
*/
if (tp->t_timer[TCPT_REXMT] == 0 ||
ptcp->th_ack != tp->snd_una)
{
tp->t_dupacks = 0;
}
else if (++tp->t_dupacks == tcprexmtthresh)
{
tcp_seq onxt = tp->snd_nxt;
u_short win =
MIN(tp->snd_wnd, tp->snd_cwnd) / 2 /
tp->t_maxseg;
if (win < 2)
win = 2;
tp->snd_ssthresh = (u_short)(win * tp->t_maxseg);
tp->t_timer[TCPT_REXMT] = 0;
tp->t_rtt = 0;
tp->snd_nxt = ptcp->th_ack;
tp->snd_cwnd = tp->t_maxseg;
(void) tcp_output(tp);
if (SEQ_GT(onxt, tp->snd_nxt))
tp->snd_nxt = onxt;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -