/* tcp_input.c 1.26 81/11/20 */
#include "../h/socketvar.h"
#include "../net/inet_pcb.h"
#include "../net/inet_systm.h"
#include "../net/ip_var.h"
#include "../net/tcp_fsm.h"
#include "../net/tcp_var.h"
#include "/usr/include/errno.h"
register struct inpcb
*ip
;
for (ip
= tcb
.inp_next
; ip
!= &tcb
; ip
= ip
->inp_next
)
tcp_drainunack(intotcpcb(ip
));
register struct tcpcb
*tp
;
for (m
= tp
->seg_unack
; m
; m
= m
->m_act
)
struct sockaddr_in tcp_sockaddr
= { AF_INET
};
register struct tcpiphdr
*ti
;
register struct tcpcb
*tp
;
* Get ip and tcp header together in first mbuf.
ti
= mtod(m
, struct tcpiphdr
*);
if (ti
->ti_len
> sizeof (struct ip
))
ip_stripoptions((struct ip
*)ti
, (char *)0);
if (m
->m_len
< sizeof (struct tcpiphdr
) &&
m_pullup(m
, sizeof (struct tcpiphdr
)) == 0) {
* 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
= htons((u_short
)tlen
);
if (ti
->ti_sum
= inet_cksum(m
, len
)) {
printf("tcp cksum %x\ti", ti
->ti_sum
);
* Check that tcp offset makes sense,
* process tcp options and adjust length.
if (off
< sizeof (struct tcphdr
) || off
> ti
->ti_len
) {
* Convert addresses and ports to host format.
* Locate pcb for segment.
ti
->ti_src
.s_addr
= ntohl(ti
->ti_src
.s_addr
);
ti
->ti_dst
.s_addr
= ntohl(ti
->ti_dst
.s_addr
);
ti
->ti_sport
= ntohs(ti
->ti_sport
);
ti
->ti_dport
= ntohs(ti
->ti_dport
);
inp
= in_pcblookup(&tcb
, ti
->ti_src
, ti
->ti_sport
, ti
->ti_dst
, ti
->ti_dport
);
tp
= intotcpcb(inp
); /* ??? */
goto notwanted
; /* ??? */
* Convert tcp protocol specific fields to host format.
ti
->ti_seq
= ntohl(ti
->ti_seq
);
ti
->ti_ackno
= ntohl((n_long
)ti
->ti_ackno
);
ti
->ti_win
= ntohs(ti
->ti_win
);
ti
->ti_urp
= ntohs(ti
->ti_urp
);
* Check segment seq # and do rst processing
if ((tiflags
&TH_ACK
) || (tiflags
&TH_SYN
) == 0) {
if (!ack_ok(tp
, ti
) || (tiflags
&TH_SYN
) == 0) {
tcp_sndrst(tp
, ti
); /* 71,72,75 */
tcp_error(tp
, ENETRESET
);
if ((tiflags
&TH_RST
) == 0)
if (ti
->ti_seq
< tp
->rcv_nxt
) /* bad rst */
inp
->inp_faddr
.s_addr
= 0;
tcp_error(tp
, ENETRESET
);
if (ack_ok(tp
, ti
) == 0) {
tcp_sndrst(tp
, ti
); /* 74 */
if ((tiflags
&TH_SYN
) == 0 && ti
->ti_seq
!= tp
->irs
) {
tcp_sndnull(tp
); /* 74 */
* Defer processing if no buffer space for this connection.
if (so
->so_rcv
.sb_cc
>= so
->so_rcv
.sb_hiwat
&&
ti
->ti_len
!= 0 && mbstat
.m_bufs
< mbstat
.m_lowat
) {
m->m_act = (struct mbuf *)0;
if ((m = tp->seg_unack) != NULL) {
* Discard ip header, and do tcp input processing.
off
+= sizeof (struct ip
);
tp
->tc_flags
&= ~TC_NET_KEEP
;
acounts
[tp
->t_state
][INRECV
]++;
if ((tp
->t_socket
->so_options
& SO_DEBUG
) || tcpconsdebug
) {
tdb_setup(tp
, ti
, INRECV
, &tdb
);
tcp_sockaddr
.sin_addr
= ti
->ti_src
;
tcp_sockaddr
.sin_port
= ti
->ti_sport
;
if ((tiflags
&TH_SYN
) == 0 || in_pcbsetpeer(inp
, &tcp_sockaddr
)) {
tp
->t_template
= tcp_template(tp
);
if (tp
->tc_flags
&TC_FIN_RCVD
) {
tp
->t_finack
= T_2ML
; /* 3 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
tp
->t_init
= T_INIT
/ 2; /* 4 */
if (tp
->tc_flags
&TC_FIN_RCVD
) {
if ((tiflags
&TH_ACK
) == 0) {
tp
->t_finack
= T_2ML
; /* 9 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
nstate
= (tiflags
&TH_ACK
) ? ESTAB
: SYN_RCVD
; /* 11:8 */
if ((tiflags
&TH_ACK
) == 0 ||
(tiflags
&TH_ACK
) && ti
->ti_ackno
<= tp
->iss
) {
tcp_ctldat(tp
, ti
, 1); /* 39 */
if (tp
->tc_flags
&TC_FIN_RCVD
)
nstate
= (tp
->tc_flags
&TC_FIN_RCVD
) ?
CLOSE_WAIT
: ESTAB
; /* 33:5 */
if ((tp
->tc_flags
& TC_FIN_RCVD
) == 0) {
nstate
= FIN_W2
; /* 27 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
nstate
= j
? TIME_WAIT
: CLOSING
; /* 28:26 */
if (tp
->tc_flags
&TC_FIN_RCVD
) {
tp
->t_finack
= T_2ML
; /* 29 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
ti
->ti_ackno
<= tp
->seq_fin
) {
tcp_ctldat(tp
, ti
, 0); /* 30 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
(void) tcp_sndctl(tp
); /* 31 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
nstate
= TIME_WAIT
; /* 23 */
if (tp
->tc_flags
&TC_WAITED_2_ML
)
sorwakeup(inp
->inp_socket
);
nstate
= CLOSED
; /* 15 */
nstate
= RCV_WAIT
; /* 18 */
if (rcv_empty(tp
)) { /* 16 */
sorwakeup(inp
->inp_socket
);
nstate
= RCV_WAIT
; /* 19 */
(void) tcp_sndctl(tp
); /* 31 */
if ((tiflags
&TH_FIN
) && (tiflags
&TH_ACK
) &&
ti
->ti_ackno
<= tp
->seq_fin
) {
tp
->tc_flags
&= ~TC_WAITED_2_ML
; /* 30 */
* Done with state*input specific processing.
* Form trace records, free input if not needed,
/* IF CLOSED CANT LOOK AT tc_flags */
if ((tp
->tc_flags
&TC_NET_KEEP
) == 0) {
/* inline expansion of m_freem */
* Unwanted packed; free everything
* but the header and return an rst.
m
->m_len
= sizeof(struct tcpiphdr
);
#define xchg(a,b) j=a; a=b; b=j
xchg(ti
->ti_dst
.s_addr
, ti
->ti_src
.s_addr
);
xchg(ti
->ti_dport
, ti
->ti_sport
);
ti
->ti_seq
= ti
->ti_ackno
;
ti
->ti_ackno
= htonl((unsigned)(ntohl(ti
->ti_seq
) + ti
->ti_len
));
ti
->ti_flags
= ((tiflags
& TH_ACK
) ? 0 : TH_ACK
) | TH_RST
;
ti
->ti_len
= htons(TCPSIZE
);
ti
->ti_sum
= inet_cksum(m
, sizeof(struct tcpiphdr
));
((struct ip
*)ti
)->ip_len
= sizeof(struct tcpiphdr
);
tcp_ctldat(tp
, n0
, dataok
)
register struct tcpcb
*tp
;
register struct tcpiphdr
*ti
= n0
;
register int tiflags
= ti
->ti_flags
;
struct socket
*so
= tp
->t_inpcb
->inp_socket
;
seq_t past
= ti
->ti_seq
+ ti
->ti_len
;
urgent
= ti
->ti_seq
+ ti
->ti_urp
;
tp
->tc_flags
&= ~(TC_ACK_DUE
|TC_NEW_WINDOW
);
if ((tp
->tc_flags
&TC_SYN_RCVD
) == 0 && (tiflags
&TH_SYN
)) {
tp
->rcv_nxt
= ti
->ti_seq
+ 1;
tp
->snd_wl
= tp
->rcv_urp
= tp
->irs
;
tp
->tc_flags
|= (TC_SYN_RCVD
|TC_ACK_DUE
);
if ((tiflags
&TH_ACK
) && (tp
->tc_flags
&TC_SYN_RCVD
) &&
ti
->ti_ackno
> tp
->snd_una
) {
* Reflect newly acknowledged data.
tp
->snd_una
= ti
->ti_ackno
;
if (tp
->snd_una
> tp
->snd_nxt
)
tp
->snd_nxt
= tp
->snd_una
;
* If timed msg acked, update retransmit time value.
if ((tp
->tc_flags
&TC_SYN_ACKED
) &&
tp
->snd_una
> tp
->t_xmt_val
) {
/* NEED SMOOTHING HERE */
tp
->t_xmtime
= (tp
->t_xmt
!= 0 ? tp
->t_xmt
: T_REXMT
);
if (tp
->t_xmtime
> T_REMAX
)
* Remove acked data from send buf
sbdrop(&so
->so_snd
, (int)(tp
->snd_una
- tp
->snd_off
));
tp
->snd_off
= tp
->snd_una
;
if ((tp
->tc_flags
&TC_SYN_ACKED
) == 0 &&
(tp
->snd_una
> tp
->iss
)) {
tp
->tc_flags
|= TC_SYN_ACKED
;
if (tp
->seq_fin
!= tp
->iss
&& tp
->snd_una
> tp
->seq_fin
)
tp
->tc_flags
&= ~TC_SND_FIN
;
tp
->tc_flags
|= TC_CANCELLED
;
sowwakeup(tp
->t_inpcb
->inp_socket
);
if ((tp
->tc_flags
& TC_SYN_RCVD
) && ti
->ti_seq
>= tp
->snd_wl
) {
tp
->snd_wnd
= ti
->ti_win
;
tp
->tc_flags
|= TC_NEW_WINDOW
;
if (dataok
&& ti
->ti_len
) {
register struct tcpiphdr
*q
;
for (m
= dtom(ti
); m
->m_next
; m
= m
->m_next
)
m
->m_act
= (struct mbuf
*)(mtod(m
, caddr_t
) - 1);
* Discard duplicate data already passed to user.
if (SEQ_LT(ti
->ti_seq
, tp
->rcv_nxt
)) {
register int i
= tp
->rcv_nxt
- ti
->ti_seq
;
* 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
;
/* w/o setting TC_NET_KEEP */
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.
tp
->seqcnt
+= ti
->ti_len
;
* Calculate available space and discard segments for
* which there is too much.
(so
->so_rcv
.sb_cc
/*XXX+tp->rcv_seqcnt*/) - so
->so_rcv
.sb_hiwat
;
register int i
= MIN(q
->ti_len
, overage
);
panic("tcp_text dropall");
q
= (struct tcpiphdr
*)q
->ti_prev
;
* Advance rcv_next through newly completed sequence space.
while (ti
->ti_seq
== tp
->rcv_nxt
) {
tp
->rcv_nxt
+= ti
->ti_len
;
ti
= (struct tcpiphdr
*)ti
->ti_next
;
if (ti
== (struct tcpiphdr
*)tp
)
if (SEQ_GT(urgent
, tp
->rcv_urp
))
tp
->tc_flags
|= (TC_ACK_DUE
|TC_NET_KEEP
);
if ((tiflags
&TH_FIN
) && past
== tp
->rcv_nxt
) {
if ((tp
->tc_flags
&TC_FIN_RCVD
) == 0) {
tp
->tc_flags
|= TC_FIN_RCVD
;
tp
->tc_flags
|= TC_ACK_DUE
;
if (tp
->tc_flags
&TC_ACK_DUE
)
else if ((tp
->tc_flags
&TC_NEW_WINDOW
))
if (tp
->snd_nxt
<= tp
->snd_off
+ so
->so_snd
.sb_cc
||
(tp
->tc_flags
&TC_SND_FIN
))
if (!sent
&& tp
->snd_una
< tp
->snd_nxt
&&
(tp
->tc_flags
&TC_CANCELLED
)) {
tp
->t_rexmt
= tp
->t_xmtime
;
tp
->t_rexmttl
= T_REXMTTL
;
tp
->t_rexmt_val
= tp
->t_rtl_val
= tp
->snd_lst
;
tp
->tc_flags
&= ~TC_CANCELLED
;
/* present data to user */
if ((tp
->tc_flags
&TC_SYN_ACKED
) == 0)
while (ti
!= (struct tcpiphdr
*)tp
&& ti
->ti_seq
< tp
->rcv_nxt
) {
sbappend(&so
->so_rcv
, dtom(ti
));
tp
->seqcnt
-= ti
->ti_len
;
panic("tcp_input present");
ti
= (struct tcpiphdr
*)ti
->ti_next
;