📄 tcp_input.c
字号:
rnd = (314159 * rnd + 66329) & 0xffff;
j = ((qlen + 1) * rnd) >> 16;
while (j-- && so2)
so2 = so2->so_q0;
}
if (so2) {
tcp_drop(sototcpcb(so2), ETIMEDOUT);
so2 = sonewconn(so, 0);
}
if (!so2)
goto drop;
}
so = so2;
/*
* This is ugly, but ....
*
* Mark socket as temporary until we're
* committed to keeping it. The code at
* ``drop'' and ``dropwithreset'' check the
* flag dropsocket to see if the temporary
* socket created here should be discarded.
* We mark the socket as discardable until
* we're committed to it below in TCPS_LISTEN.
*/
dropsocket++;
inp = (struct inpcb *)so->so_pcb;
inp->inp_laddr = ti->ti_dst;
inp->inp_lport = ti->ti_dport;
in_pcbrehash(inp);
#if BSD>=43
inp->inp_options = ip_srcroute();
#endif
tp = intotcpcb(inp);
tp->t_state = TCPS_LISTEN;
tp->t_flags |= tp0->t_flags & (TF_NOPUSH|TF_NOOPT);
/* Compute proper scaling value from buffer space */
while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
TCP_MAXWIN << tp->request_r_scale < so->so_rcv.sb_hiwat)
tp->request_r_scale++;
}
}
/*
* Segment received on connection.
* Reset idle time and keep-alive timer.
*/
tp->t_idle = 0;
if (TCPS_HAVEESTABLISHED(tp->t_state))
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
/*
* Process options if not in LISTEN state,
* else do it below (after getting remote address).
*/
if (tp->t_state != TCPS_LISTEN)
tcp_dooptions(tp, optp, optlen, ti, &to);
/*
* Header prediction: check for the two common cases
* of a uni-directional data xfer. If the packet has
* no control flags, is in-sequence, the window didn't
* change and we're not retransmitting, it's a
* candidate. If the length is zero and the ack moved
* forward, we're the sender side of the xfer. Just
* free the data acked & wake any higher level process
* that was blocked waiting for space. If the length
* is non-zero and the ack didn't move, we're the
* receiver side. If we're getting packets in-order
* (the reassembly queue is empty), add the data to
* the socket buffer and note that we need a delayed ack.
* Make sure that the hidden state-flags are also off.
* Since we check for TCPS_ESTABLISHED above, it can only
* be TH_NEEDSYN.
*/
if (tp->t_state == TCPS_ESTABLISHED &&
(tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
((tp->t_flags & (TF_NEEDSYN|TF_NEEDFIN)) == 0) &&
((to.to_flag & TOF_TS) == 0 ||
TSTMP_GEQ(to.to_tsval, tp->ts_recent)) &&
/*
* Using the CC option is compulsory if once started:
* the segment is OK if no T/TCP was negotiated or
* if the segment has a CC option equal to CCrecv
*/
((tp->t_flags & (TF_REQ_CC|TF_RCVD_CC)) != (TF_REQ_CC|TF_RCVD_CC) ||
((to.to_flag & TOF_CC) != 0 && to.to_cc == tp->cc_recv)) &&
ti->ti_seq == tp->rcv_nxt &&
tiwin && tiwin == tp->snd_wnd &&
tp->snd_nxt == tp->snd_max) {
/*
* If last ACK falls within this segment's sequence numbers,
* record the 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 (ti->ti_len == 0) {
if (SEQ_GT(ti->ti_ack, tp->snd_una) &&
SEQ_LEQ(ti->ti_ack, tp->snd_max) &&
tp->snd_cwnd >= tp->snd_wnd) {
/*
* this is a pure ack for outstanding data.
*/
++tcpstat.tcps_predack;
if ((to.to_flag & TOF_TS) != 0)
tcp_xmit_timer(tp,
tcp_now - to.to_tsecr + 1);
else if (tp->t_rtt &&
SEQ_GT(ti->ti_ack, tp->t_rtseq))
tcp_xmit_timer(tp, tp->t_rtt);
acked = ti->ti_ack - tp->snd_una;
tcpstat.tcps_rcvackpack++;
tcpstat.tcps_rcvackbyte += acked;
sbdrop(&so->so_snd, acked);
tp->snd_una = ti->ti_ack;
m_freem(m);
/*
* If all outstanding data are acked, stop
* retransmit timer, otherwise restart timer
* using current (possibly backed-off) value.
* If process is waiting for space,
* wakeup/selwakeup/signal. If data
* are ready to send, let tcp_output
* decide between more output or persist.
*/
if (tp->snd_una == tp->snd_max)
tp->t_timer[TCPT_REXMT] = 0;
else if (tp->t_timer[TCPT_PERSIST] == 0)
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
if (so->so_snd.sb_flags & SB_NOTIFY)
sowwakeup(so);
if (so->so_snd.sb_cc)
(void) tcp_output(tp);
return;
}
} else if (ti->ti_ack == tp->snd_una &&
tp->seg_next == (struct tcpiphdr *)tp &&
ti->ti_len <= sbspace(&so->so_rcv)) {
/*
* this is a pure, in-sequence data packet
* with nothing on the reassembly queue and
* we have enough buffer space to take it.
*/
++tcpstat.tcps_preddat;
tp->rcv_nxt += ti->ti_len;
OS_DbgPrint(OSK_MID_TRACE,("Added %d to rcv_nxt\n", ti->ti_len - sizeof(struct ip)));
tcpstat.tcps_rcvpack++;
tcpstat.tcps_rcvbyte += ti->ti_len;
/*
* Add data to socket buffer.
*/
sbappend(&so->so_rcv, m);
sorwakeup(so);
#ifdef TCP_ACK_HACK
/*
* If this is a short packet, then ACK now - with Nagel
* congestion avoidance sender won't send more until
* he gets an ACK.
*/
if (tiflags & TH_PUSH) {
tp->t_flags |= TF_ACKNOW;
tcp_output(tp);
} else {
tp->t_flags |= TF_DELACK;
}
#else
tp->t_flags |= TF_DELACK;
#endif
return;
}
}
/*
* 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 = sbspace(&so->so_rcv);
if (win < 0)
win = 0;
tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt));
}
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: {
struct mbuf *am;
register struct sockaddr_in *sin;
if (tiflags & TH_RST)
goto drop;
if (tiflags & TH_ACK)
goto dropwithreset;
if ((tiflags & TH_SYN) == 0)
goto drop;
/*
* RFC1122 4.2.3.10, p. 104: discard bcast/mcast SYN
* in_broadcast() should never return true on a received
* packet with M_BCAST not set.
*/
if (m->m_flags & (M_BCAST|M_MCAST) ||
IN_MULTICAST(ntohl(ti->ti_dst.s_addr)))
goto drop;
am = m_get(M_DONTWAIT, MT_SONAME); /* XXX */
if (am == NULL)
goto drop;
am->m_len = sizeof (struct sockaddr_in);
sin = mtod(am, struct sockaddr_in *);
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr = ti->ti_src;
sin->sin_port = ti->ti_sport;
bzero((caddr_t)sin->sin_zero, sizeof(sin->sin_zero));
laddr = inp->inp_laddr;
if (inp->inp_laddr.s_addr == INADDR_ANY)
inp->inp_laddr = ti->ti_dst;
if (in_pcbconnect(inp, am)) {
inp->inp_laddr = laddr;
(void) m_free(am);
goto drop;
}
(void) m_free(am);
tp->t_template = tcp_template(tp);
if (tp->t_template == 0) {
tp = tcp_drop(tp, ENOBUFS);
dropsocket = 0; /* socket is already gone */
goto drop;
}
if ((taop = tcp_gettaocache(inp)) == NULL) {
taop = &tao_noncached;
bzero(taop, sizeof(*taop));
}
tcp_dooptions(tp, optp, optlen, ti, &to);
if (iss)
tp->iss = iss;
else
tp->iss = tcp_iss;
tcp_iss += TCP_ISSINCR/2;
tp->irs = ti->ti_seq;
tcp_sendseqinit(tp);
tcp_rcvseqinit(tp);
/*
* Initialization of the tcpcb for transaction;
* set SND.WND = SEG.WND,
* initialize CCsend and CCrecv.
*/
tp->snd_wnd = tiwin; /* initial send-window */
tp->cc_send = CC_INC(tcp_ccgen);
tp->cc_recv = to.to_cc;
/*
* Perform TAO test on incoming CC (SEG.CC) option, if any.
* - compare SEG.CC against cached CC from the same host,
* if any.
* - if SEG.CC > chached value, SYN must be new and is accepted
* immediately: save new CC in the cache, mark the socket
* connected, enter ESTABLISHED state, turn on flag to
* send a SYN in the next segment.
* A virtual advertised window is set in rcv_adv to
* initialize SWS prevention. Then enter normal segment
* processing: drop SYN, process data and FIN.
* - otherwise do a normal 3-way handshake.
*/
if ((to.to_flag & TOF_CC) != 0) {
if (taop->tao_cc != 0 && CC_GT(to.to_cc, taop->tao_cc)) {
taop->tao_cc = to.to_cc;
tp->t_state = TCPS_ESTABLISHED;
/*
* If there is a FIN, or if there is data and the
* connection is local, then delay SYN,ACK(SYN) in
* the hope of piggy-backing it on a response
* segment. Otherwise must send ACK now in case
* the other side is slow starting.
*/
if ((tiflags & TH_FIN) || (ti->ti_len != 0 &&
in_localaddr(inp->inp_faddr)))
tp->t_flags |= (TF_DELACK | TF_NEEDSYN);
else
tp->t_flags |= (TF_ACKNOW | TF_NEEDSYN);
/*
* Limit the `virtual advertised window' to TCP_MAXWIN
* here. Even if we requested window scaling, it will
* become effective only later when our SYN is acked.
*/
tp->rcv_adv += min(tp->rcv_wnd, TCP_MAXWIN);
tcpstat.tcps_connects++;
soisconnected(so);
tp->t_timer[TCPT_KEEP] = tcp_keepinit;
dropsocket = 0; /* committed to socket */
tcpstat.tcps_accepts++;
goto trimthenstep6;
}
/* else do standard 3-way handshake */
} else {
/*
* No CC option, but maybe CC.NEW:
* invalidate cached value.
*/
taop->tao_cc = 0;
}
/*
* TAO test failed or there was no CC option,
* do a standard 3-way handshake.
*/
tp->t_flags |= TF_ACKNOW;
tp->t_state = TCPS_SYN_RECEIVED;
tp->t_timer[TCPT_KEEP] = tcp_keepinit;
dropsocket = 0; /* committed to socket */
tcpstat.tcps_accepts++;
goto trimthenstep6;
}
/*
* If the state is SYN_RECEIVED:
* do just the ack and RST checks from SYN_SENT state.
* 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 contains 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_RECEIVED:
case TCPS_SYN_SENT:
if ((taop = tcp_gettaocache(inp)) == NULL) {
taop = &tao_noncached;
bzero(taop, sizeof(*taop));
}
if ((tiflags & TH_ACK) &&
(SEQ_LEQ(ti->ti_ack, tp->iss) ||
SEQ_GT(ti->ti_ack, tp->snd_max))) {
/*
* If we have a cached CCsent for the remote host,
* hence we haven't just crashed and restarted,
* do not send a RST. This may be a retransmission
* from the other side after our earlier ACK was lost.
* Our new SYN, when it arrives, will serve as the
* needed ACK.
*/
if (taop->tao_ccsent != 0)
goto drop;
else
goto dropwithreset;
}
if (tiflags & TH_RST) {
if (tiflags & TH_ACK)
tp = tcp_drop(tp, ECONNREFUSED);
goto drop;
}
if (tp->t_state == TCPS_SYN_RECEIVED)
break;
if ((tiflags & TH_SYN) == 0)
goto drop;
tp->snd_wnd = ti->ti_win; /* initial send window */
tp->cc_recv = to.to_cc; /* foreign CC */
tp->irs = ti->ti_seq;
tcp_rcvseqinit(tp);
if (tiflags & TH_ACK) {
/*
* Our SYN was acked. If segment contains CC.ECHO
* option, check it to make sure this segment really
* matches our SYN. If not, just drop it as old
* duplicate, but send an RST if we're still playing
* by the old rules.
*/
if ((to.to_flag & TOF_CCECHO) &&
tp->cc_send != to.to_ccecho) {
if (taop->tao_ccsent != 0)
goto drop;
else
goto dropwithreset;
}
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 (ti->ti_len != 0)
tp->t_flags |= TF_DELACK;
else
tp->t_flags |= TF_ACKNOW;
/*
* Received <SYN,ACK> in SYN_SENT[*] state.
* Transitions:
* SYN_SENT --> ESTABLISHED
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -