/* tcp_input.c 1.40 81/12/12 */
#include "../h/protosw.h"
#include "../h/socketvar.h"
#include "../net/in_pcb.h"
#include "../net/in_systm.h"
#include "../net/ip_var.h"
#include "../net/tcp_fsm.h"
#include "../net/tcp_seq.h"
#include "../net/tcp_timer.h"
#include "../net/tcp_var.h"
#include "../net/tcpip.h"
struct sockaddr_in tcp_in
= { AF_INET
};
* TCP input routine, follows pages 65-76 of the
* protocol specification dated September, 1981 very closely.
register struct tcpiphdr
*ti
;
register struct tcpcb
*tp
;
* Get IP and TCP header together in first mbuf.
* Note: IP leaves IP header in first mbuf.
ti
= mtod(m
, struct tcpiphdr
*);
if (((struct ip
*)ti
)->ip_hl
> (sizeof (struct ip
) >> 2))
ip_stripoptions((struct ip
*)ti
, (struct mbuf
*)0);
if (m
->m_len
< sizeof (struct tcpiphdr
)) {
if (m_pullup(m
, sizeof (struct tcpiphdr
)) == 0) {
ti
= mtod(m
, struct tcpiphdr
*);
* Checksum extended TCP header and data.
tlen
= ((struct ip
*)ti
)->ip_len
;
len
= sizeof (struct ip
) + tlen
;
ti
->ti_next
= ti
->ti_prev
= 0;
ti
->ti_len
= (u_short
)tlen
;
ti
->ti_len
= htons(ti
->ti_len
);
if (ti
->ti_sum
= in_cksum(m
, len
)) {
printf("tcp cksum %x\n", ti
->ti_sum
);
* Check that TCP offset makes sense,
* process TCP options and adjust length.
if (off
< sizeof (struct tcphdr
) || off
> tlen
) {
if (off
> sizeof (struct tcphdr
))
* Convert TCP protocol specific fields to host format.
ti
->ti_seq
= ntohl(ti
->ti_seq
);
ti
->ti_ack
= ntohl(ti
->ti_ack
);
ti
->ti_win
= ntohs(ti
->ti_win
);
ti
->ti_urp
= ntohs(ti
->ti_urp
);
* Locate pcb for segment.
(&tcb
, ti
->ti_src
, ti
->ti_sport
, ti
->ti_dst
, ti
->ti_dport
);
* If the state is CLOSED (i.e., TCB does not exist) then
* all data in the incoming segment is discarded.
* Segment received on connection.
* Reset idle time and keep-alive timer.
tp
->t_timer
[TCPT_KEEP
] = TCPTV_KEEP
;
* Calculate amount of space in receive window,
* and then do TCP input processing.
tp
->rcv_wnd
= sbspace(&so
->so_rcv
);
* 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.
* 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
if ((tiflags
& TH_SYN
) == 0)
tcp_in
.sin_addr
= ti
->ti_src
;
tcp_in
.sin_port
= ti
->ti_sport
;
if (in_pcbconnect(inp
, (struct sockaddr
*)&tcp_in
))
tp
->t_template
= tcp_template(tp
);
if (tp
->t_template
== 0) {
tp
->iss
= tcp_iss
; tcp_iss
+= TCP_ISSINCR
/2;
tp
->t_state
= TCPS_SYN_RECEIVED
;
tp
->t_timer
[TCPT_KEEP
] = TCPTV_KEEP
;
* 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
if ((tiflags
& TH_ACK
) &&
(SEQ_LEQ(ti
->ti_ack
, tp
->iss
) ||
SEQ_GT(ti
->ti_ack
, tp
->snd_max
)))
tcp_drop(tp
, ECONNRESET
);
if ((tiflags
& TH_SYN
) == 0)
tp
->snd_una
= ti
->ti_ack
;
tp
->t_timer
[TCPT_REXMT
] = 0;
tp
->t_flags
|= TF_ACKNOW
;
if (SEQ_GT(tp
->snd_una
, tp
->iss
)) {
tp
->t_state
= TCPS_ESTABLISHED
;
(void) tcp_reass(tp
, (struct tcpiphdr
*)0);
tp
->snd_wl1
= ti
->ti_seq
;
tp
->t_state
= TCPS_SYN_RECEIVED
;
* Advance ti->ti_seq to correspond to first data byte.
* If data, trim to stay within window,
* dropping FIN if necessary.
if (ti
->ti_len
> tp
->rcv_wnd
) {
todrop
= ti
->ti_len
- tp
->rcv_wnd
;
ti
->ti_len
= tp
->rcv_wnd
;
* States other than LISTEN or SYN_SENT.
* First check that at least some bytes of segment are within
* If window is closed can only take segments at
* window edge, and have to drop data and PUSH from
if (tp
->rcv_nxt
!= ti
->ti_seq
)
ti
->ti_flags
&= ~(TH_PUSH
|TH_FIN
);
* If segment begins before rcv_nxt, drop leading
* data (and SYN); if nothing left, just ack.
if (SEQ_GT(tp
->rcv_nxt
, ti
->ti_seq
)) {
todrop
= tp
->rcv_nxt
- ti
->ti_seq
;
/* ti->ti_flags &= ~TH_URG; */
/* tiflags &= ~TH_SYN; */
/* ti->ti_flags &= ~TH_SYN; */
* If segment ends after window, drop trailing data
* (and PUSH and FIN); if nothing left, just ACK.
if (SEQ_GT(ti
->ti_seq
+ti
->ti_len
, tp
->rcv_nxt
+tp
->rcv_wnd
)) {
ti
->ti_seq
+ti
->ti_len
- (tp
->rcv_nxt
+tp
->rcv_wnd
);
ti
->ti_flags
&= ~(TH_PUSH
|TH_FIN
);
* If the RST bit is set examine the 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
if (tiflags
&TH_RST
) switch (tp
->t_state
) {
if (inp
->inp_socket
->so_options
& SO_ACCEPTCONN
) {
tp
->t_state
= TCPS_LISTEN
;
tp
->t_timer
[TCPT_KEEP
] = 0;
(void) m_free(dtom(tp
->t_template
));
tcp_drop(tp
, ECONNREFUSED
);
tcp_drop(tp
, ECONNRESET
);
* If a SYN is in the window, then this is an
* error and we send an RST and drop the connection.
tcp_drop(tp
, ECONNRESET
);
* If the ACK bit is off we drop the segment and return.
if ((tiflags
& TH_ACK
) == 0)
* In SYN_RECEIVED state if the ack ACKs our SYN then enter
* ESTABLISHED state and continue processing, othewise
if (SEQ_GT(tp
->snd_una
, ti
->ti_ack
) ||
SEQ_GT(ti
->ti_ack
, tp
->snd_max
))
tp
->snd_una
++; /* SYN acked */
tp
->t_timer
[TCPT_REXMT
] = 0;
tp
->t_state
= TCPS_ESTABLISHED
;
(void) tcp_reass(tp
, (struct tcpiphdr
*)0);
tp
->snd_wl1
= ti
->ti_seq
- 1;
* 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.
#define ourfinisacked (acked > 0)
if (SEQ_LEQ(ti
->ti_ack
, tp
->snd_una
))
if (SEQ_GT(ti
->ti_ack
, tp
->snd_max
))
acked
= ti
->ti_ack
- tp
->snd_una
;
if (acked
>= so
->so_snd
.sb_cc
) {
acked
-= so
->so_snd
.sb_cc
;
/* if acked > 0 our FIN is acked */
sbdrop(&so
->so_snd
, so
->so_snd
.sb_cc
);
tp
->t_timer
[TCPT_REXMT
] = 0;
sbdrop(&so
->so_snd
, acked
);
TCPT_RANGESET(tp
->t_timer
[TCPT_REXMT
],
tcp_beta
* tp
->t_srtt
, TCPTV_MIN
, TCPTV_MAX
);
tp
->snd_una
= ti
->ti_ack
;
* If transmit timer is running and timed sequence
* number was acked, update smoothed round trip time.
if (tp
->t_rtt
&& SEQ_GT(ti
->ti_ack
, tp
->t_rtseq
)) {
(1 - tcp_alpha
) * tp
->t_rtt
;
* In FIN_WAIT_1 STATE in addition to the processing
* for the ESTABLISHED state if our FIN is now acknowledged
tp
->t_state
= TCPS_FIN_WAIT_2
;
* 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
tp
->t_state
= TCPS_TIME_WAIT
;
tp
->t_timer
[TCPT_2MSL
] = 2 * TCPTV_MSL
;
* The only thing that can arrive in LAST_ACK state
* is an acknowledgment of our FIN. If our FIN is now
* acknowledged, delete the TCB, enter the closed state
* In TIME_WAIT state the only thing that should arrive
* is a retransmission of the remote FIN. Acknowledge
* it and restart the finack timer.
tp
->t_timer
[TCPT_2MSL
] = 2 * TCPTV_MSL
;
* Update window information.
if (SEQ_LT(tp
->snd_wl1
, ti
->ti_seq
) ||
tp
->snd_wl1
==ti
->ti_seq
&& SEQ_LEQ(tp
->snd_wl2
,ti
->ti_seq
)) {
tp
->snd_wnd
= ti
->ti_win
;
tp
->snd_wl1
= ti
->ti_seq
;
tp
->snd_wl2
= ti
->ti_ack
;
tp
->t_timer
[TCPT_PERSIST
] = 0;
* If an URG bit is set in the segment and is greater than the
* current known urgent pointer, then signal the user that the
* remote side has out of band data. This should not happen
* in CLOSE_WAIT, CLOSING, LAST-ACK or TIME_WAIT STATES since
* a FIN has been received from the remote side. In these states
if ((tiflags
& TH_URG
) == 0 && TCPS_HAVERCVDFIN(tp
->t_state
) == 0)
if (SEQ_GT(ti
->ti_urp
, tp
->rcv_up
)) {
sohasoutofband(so
); /* XXX */
* Process the segment text, merging it into the TCP sequencing queue,
* and arranging for acknowledgment of receipt if necessary.
* This process logically involves adjusting tp->rcv_wnd as data
* is presented to the user (this happens in tcp_usrreq.c,
* case PRU_RCVD). If a FIN has already been received on this
* connection then we just ignore the text.
if (ti
->ti_len
&& TCPS_HAVERCVDFIN(tp
->t_state
) == 0) {
off
+= sizeof (struct ip
); /* drop IP header */
tiflags
= tcp_reass(tp
, ti
);
tp
->t_flags
|= TF_ACKNOW
; /* XXX TF_DELACK */
* If FIN is received then if we haven't received SYN and
* therefore can't validate drop the segment. Otherwise ACK
* the FIN and let the user know that the connection is closing.
if ((tiflags
& TH_FIN
)) {
if (TCPS_HAVERCVDSYN(tp
->t_state
) == 0)
if (TCPS_HAVERCVDFIN(tp
->t_state
) == 0) {
tp
->t_flags
|= TF_ACKNOW
;
* In SYN_RECEIVED and ESTABLISHED STATES
* enter the CLOSE_WAIT state.
tp
->t_state
= TCPS_CLOSE_WAIT
;
* If still in FIN_WAIT_1 STATE FIN has not been acked so
* enter the CLOSING state.
tp
->t_state
= TCPS_CLOSING
;
* In FIN_WAIT_2 state enter the TIME_WAIT state,
* starting the time-wait timer, turning off the other
tp
->t_state
= TCPS_TIME_WAIT
;
tp
->t_timer
[TCPT_2MSL
] = 2 * TCPTV_MSL
;
* In TIME_WAIT state restart the 2 MSL time_wait timer.
tp
->t_timer
[TCPT_2MSL
] = 2 * TCPTV_MSL
;
* Return any desired output.
* Generate an ACK dropping incoming segment.
* Make ACK reflect our state.
tcp_respond(ti
, tp
->rcv_nxt
, tp
->snd_nxt
, TH_ACK
);
* Generate a RST, dropping incoming segment.
* Make ACK acceptable to originator of segment.
tcp_respond(ti
, (tcp_seq
)0, ti
->ti_ack
, TH_RST
);
tcp_respond(ti
, ti
->ti_seq
+ti
->ti_len
, (tcp_seq
)0, TH_RST
|TH_ACK
);
* Drop space held by incoming segment and return.
* Insert segment ti into reassembly queue of tcp with
* control block tp. Return TH_FIN if reassembly now includes
register struct tcpcb
*tp
;
register struct tcpiphdr
*ti
;
register struct tcpiphdr
*q
;
struct socket
*so
= tp
->t_inpcb
->inp_socket
;
int flags
= 0; /* no FIN */
* Call with ti==0 after become established to
* force pre-ESTABLISHED data up to user socket.
* Find a segment which begins after this one does.
for (q
= tp
->seg_next
; q
!= (struct tcpiphdr
*)tp
;
q
= (struct tcpiphdr
*)q
->ti_next
)
if (SEQ_GT(q
->ti_seq
, ti
->ti_seq
))
* If there is a preceding segment, it may provide some of
* our data already. If so, drop the data from the incoming
* segment. If it provides all of our data, drop us.
if ((struct tcpiphdr
*)q
->ti_prev
!= (struct tcpiphdr
*)tp
) {
q
= (struct tcpiphdr
*)(q
->ti_prev
);
/* conversion to int (in i) handles seq wraparound */
i
= q
->ti_seq
+ q
->ti_len
- ti
->ti_seq
;
q
= (struct tcpiphdr
*)(q
->ti_next
);
* While we overlap succeeding segments trim them or,
* if they are completely covered, dequeue them.
while (q
!= (struct tcpiphdr
*)tp
&&
SEQ_GT(ti
->ti_seq
+ ti
->ti_len
, q
->ti_seq
)) {
register int i
= (ti
->ti_seq
+ ti
->ti_len
) - q
->ti_seq
;
q
= (struct tcpiphdr
*)q
->ti_next
;
m_freem(dtom(q
->ti_prev
));
* Stick new segment in its place.
* Present data to user, advancing rcv_nxt through
* completed sequence space.
if (tp
->t_state
< TCPS_ESTABLISHED
)
while (ti
!= (struct tcpiphdr
*)tp
&& ti
->ti_seq
== tp
->rcv_nxt
) {
tp
->rcv_nxt
+= ti
->ti_len
;
flags
= ti
->ti_flags
& TH_FIN
;
sbappend(&so
->so_rcv
, dtom(ti
));
ti
= (struct tcpiphdr
*)ti
->ti_next
;
if (so
->so_state
& SS_CANTRCVMORE
)