/* tcp_input.c 1.2 81/10/25 */
#include "../bbnnet/net.h"
#include "../bbnnet/mbuf.h"
#include "../bbnnet/host.h"
#include "../bbnnet/imp.h"
#include "../bbnnet/ucb.h"
#include "../bbnnet/tcp.h"
#include "../bbnnet/ip.h"
#include "../bbnnet/fsm.h"
register struct mbuf
*mp
;
* Build extended tcp header
n
= (struct th
*)((int)mp
+ mp
->m_off
);
tlen
= ((struct ip
*)n
)->ip_len
;
/* WONT BE POSSIBLE WHEN MBUFS ARE 256 BYTES */
if ((hlen
= n
->t_off
<< 2) > mp
->m_len
)
{ printf("tcp header overflow\n"); m_freem(mp
); return; }
* Checksum extended header and data
j
= n
->t_sum
; n
->t_sum
= 0;
if (j
!= cksum(mp
, sizeof (struct ip
) + tlen
)) {
* Find tcb for message (SHOULDN'T USE LINEAR SEARCH!)
for (tp
= netcb
.n_tcb_head
; tp
!= 0; tp
= tp
->t_tcb_next
)
if (tp
->t_lport
== lport
&& tp
->t_fport
== fport
&&
tp
->t_ucb
->uc_host
->h_addr
.s_addr
== n
->t_s
.s_addr
)
for (tp
= netcb
.n_tcb_head
; tp
!= 0; tp
= tp
->t_tcb_next
)
if (tp
->t_lport
== lport
&&
(tp
->t_fport
==fport
|| tp
->t_fport
==0) &&
(tp
->t_ucb
->uc_host
->h_addr
.s_addr
== n
->t_s
.s_addr
||
tp
->t_ucb
->uc_host
->h_addr
.s_addr
== 0))
n
->t_seq
= ntohl(n
->t_seq
);
n
->t_ackno
= ntohl(n
->t_ackno
);
n
->t_win
= ntohs(n
->t_win
);
n
->t_urp
= ntohs(n
->t_urp
);
* Check segment seq # and do rst processing
if ((n
->th_flags
&TH_ACK
) || !syn_ok(tp
, n
)) {
if (!ack_ok(tp
, n
) || !syn_ok(tp
, n
)) {
send_rst(tp
, n
); /* 71,72,75 */
if (n
->th_flags
&TH_RST
) {
t_close(tp
, URESET
); /* 70 */
if ((n
->th_flags
&TH_RST
) == 0)
if (n
->t_seq
< tp
->rcv_nxt
) /* bad rst */
h_free(tp
->t_ucb
->uc_host
);
t_close(tp
, URESET
); /* 66 */
if (ack_ok(tp
, n
) == 0) {
send_rst(tp
, n
); /* 74 */
if (syn_ok(tp
, n
) && n
->t_seq
!= tp
->irs
) {
* Defer processing if no buffer space for this connection.
if ((int)up
->uc_rcv
- (int)up
->uc_rsize
<= 0
&& n
->t_len
!= 0 && netcb
.n_bufs
< netcb
.n_lowat
) {
mp
->m_act
= (struct mbuf
*)0;
if ((m
= tp
->t_rcv_unack
) != NULL
) {
* Discard ip header, and do tcp input processing.
hlen
+= sizeof(struct ip
);
tp
->tc_flags
&= ~TC_NET_KEEP
;
acounts
[tp
->t_state
][INRECV
]++;
if ((tp
->t_ucb
->uc_flags
& UDEBUG
) || tcpconsdebug
) {
tdb
.td_flg
= n
->th_flags
;
((tp
->t_ucb
->uc_host
= h_make(&n
->t_s
)) == 0)) {
tp
->t_ucb
->uc_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 (n
->th_flags
&TH_ACK
) {
if (n
->t_ackno
> tp
->iss
)
present_data(tp
); /* 32 */
tp
->t_finack
= T_2ML
; /* 9 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
if (n
->th_flags
&TH_ACK
) {
present_data(tp
); /* 11 */
nstate
= SYN_RCVD
; /* 8 */
if ((n
->th_flags
&TH_ACK
) == 0 ||
(n
->th_flags
&TH_ACK
) && n
->t_ackno
<= tp
->iss
) {
rcv_ctldat(tp
, n
, 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
: CLOSING1
; /* 28:26 */
if (tp
->tc_flags
&TC_FIN_RCVD
) {
tp
->t_finack
= T_2ML
; /* 29 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
if (n
->th_flags
&TH_FIN
) {
if ((n
->th_flags
&TH_ACK
) &&
n
->t_ackno
<= tp
->seq_fin
) {
rcv_ctldat(tp
, n
, 0); /* 30 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
if (n
->th_flags
&TH_FIN
) {
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
nstate
= TIME_WAIT
; /* 23 */
if (tp
->tc_flags
&TC_WAITED_2_ML
)
t_close(tp
, UCLOSED
); /* 15 */
nstate
= RCV_WAIT
; /* 18 */
if (rcv_empty(tp
)) { /* 16 */
nstate
= RCV_WAIT
; /* 19 */
if (n
->th_flags
&TH_FIN
) {
if ((n
->th_flags
&TH_FIN
) && (n
->th_flags
&TH_ACK
) &&
n
->t_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,
tcp_debug
[tdbx
++ % TDBSIZE
] = tdb
;
/* IF CLOSED CANT LOOK AT tc_flags */
if ((tp
->tc_flags
&TC_NET_KEEP
) == 0)
* Unwanted packed; free everything
* but the header and return an rst.
mp
->m_len
= sizeof(struct th
);
#define xchg(a,b) j=a; a=b; b=j
xchg(n
->t_d
.s_addr
, n
->t_s
.s_addr
); xchg(n
->t_dst
, n
->t_src
);
n
->t_ackno
= htonl(ntohl(n
->t_seq
) + tlen
- hlen
);
n
->th_flags
= TH_RST
; /* not TH_FIN, TH_SYN */
n
->t_len
= htons(TCPSIZE
);
n
->t_sum
= cksum(mp
, sizeof(struct th
));
((struct ip
*)n
)->ip_len
= sizeof(struct th
);
rcv_ctldat(tp
, n
, dataok
)
register struct mbuf
*m
, *mn
;
tp
->tc_flags
&= ~(TC_DROPPED_TXT
|TC_ACK_DUE
|TC_NEW_WINDOW
);
if ((tp
->tc_flags
&TC_SYN_RCVD
) == 0 && (n
->th_flags
&TH_SYN
)) {
tp
->rcv_nxt
= n
->t_seq
+ 1;
tp
->snd_wl
= tp
->rcv_urp
= tp
->irs
;
tp
->tc_flags
|= (TC_SYN_RCVD
|TC_ACK_DUE
);
if ((n
->th_flags
&TH_ACK
) && (tp
->tc_flags
&TC_SYN_RCVD
) &&
n
->t_ackno
> tp
->snd_una
) {
/* update snd_una and snd_nxt */
tp
->snd_una
= n
->t_ackno
;
if (tp
->snd_una
> tp
->snd_nxt
)
tp
->snd_nxt
= tp
->snd_una
;
/* if timed msg acked, set retrans time value */
if ((tp
->tc_flags
&TC_SYN_ACKED
) &&
tp
->snd_una
> tp
->t_xmt_val
) {
tp
->t_xmtime
= (tp
->t_xmt
!= 0 ? tp
->t_xmt
: T_REXMT
);
if (tp
->t_xmtime
> T_REMAX
)
/* remove acked data from send buf */
len
= tp
->snd_una
- tp
->snd_off
;
while (len
> 0 && m
!= NULL
)
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
;
netwakeup(tp
->t_ucb
); /* wasteful */
if ((tp
->tc_flags
& TC_SYN_RCVD
) && n
->t_seq
>= tp
->snd_wl
) {
tp
->tc_flags
|= TC_NEW_WINDOW
;
if (n
->th_flags
&TH_URG
) {
urgent
= n
->t_urp
+ n
->t_seq
;
if (tp
->rcv_nxt
< urgent
) {
if (tp
->rcv_urp
<= tp
->rcv_nxt
)
to_user(tp
->t_ucb
, UURGENT
);
if ((n
->th_flags
&TH_EOL
) &&
(tp
->tc_flags
&TC_DROPPED_TXT
) == 0 &&
tp
->t_rcv_prev
!= (struct th
*)tp
) {
m
= dtom(tp
->t_rcv_prev
);
while (m
->m_next
!= NULL
)
(struct mbuf
*)(m
->m_off
+ m
->m_len
- 1);
if ((n
->th_flags
&TH_FIN
) && (tp
->tc_flags
&TC_DROPPED_TXT
) == 0) {
if ((tp
->tc_flags
&TC_FIN_RCVD
) == 0) {
/* do we really have fin ? */
if (tp
->t_rcv_prev
== (struct th
*)tp
||
last
== t_end(tp
->t_rcv_prev
)) {
tp
->tc_flags
|= TC_FIN_RCVD
;
netwakeup(tp
->t_ucb
); /* poke */
if ((tp
->tc_flags
&TC_FIN_RCVD
) &&
tp
->rcv_nxt
= last
+ 1; /* fin seq */
tp
->tc_flags
|= TC_ACK_DUE
;
tp
->tc_flags
|= TC_ACK_DUE
;
if (tp
->tc_flags
&TC_ACK_DUE
)
else if (tp
->tc_flags
&TC_NEW_WINDOW
)
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
;
register struct th
*p
, *q
;
register struct mbuf
*m
, *n
;
/* throw away any data we have already received */
if ((i
= tp
->rcv_nxt
- t
->t_seq
) > 0) {
last
= t_end(t
); /* last seq # in incoming seg */
i
= rcv_resource(tp
); /* # buffers available to con */
/* count buffers in segment */
for (m
= dtom(t
), j
= 0; m
!= NULL
; m
= m
->m_next
)
/* not enough resources to process segment */
if (j
> i
&& netcb
.n_bufs
< netcb
.n_lowat
) {
/* if segment preceeds top of seqeuncing queue, try to take
buffers from bottom of queue */
if (q
!= (struct th
*)tp
&& tp
->rcv_nxt
< q
->t_seq
&&
for (k
=j
-i
, p
= tp
->t_rcv_prev
; k
> 0 &&
p
!= (struct th
*)tp
; k
--) {
/* if still not enough room, drop text from end of segment */
for (m
= dtom(t
); i
> 0 && m
!= NULL
; i
--)
tp
->tc_flags
|= TC_DROPPED_TXT
;
/* merge incoming data into the sequence queue */
q
= tp
->t_rcv_next
; /* -> top of sequencing queue */
/* skip frags which new doesn't overlap at end */
while ((q
!= (struct th
*)tp
) && (t
->t_seq
> t_end(q
)))
if (q
== (struct th
*)tp
) { /* frag at end of chain */
if (last
>= tp
->rcv_nxt
) {
tp
->tc_flags
|= TC_NET_KEEP
;
tcp_enq(t
, tp
->t_rcv_prev
);
/* frag doesn't overlap any on chain */
tp
->tc_flags
|= TC_NET_KEEP
;
/* new overlaps beginning of next frag only */
} else if (last
< t_end(q
)) {
if ((i
= last
- q
->t_seq
+ 1) < t
->t_len
) {
tp
->tc_flags
|= TC_NET_KEEP
;
/* new overlaps end of previous frag */
if (t
->t_seq
<= q
->t_seq
) { /* complete cover */
if ((i
= t_end(q
) - t
->t_seq
+ 1) < t
->t_len
) {
/* new overlaps at beginning of successor frags */
while ((q
!= (struct th
*)tp
) && (t
->t_len
!= 0) &&
if ((i
= last
- q
->t_seq
+ 1) < t
->t_len
) {
/* enqueue whatever is left of new before successors */
tp
->tc_flags
|= TC_NET_KEEP
;
/* set to ack completed data (no gaps) */
tp
->rcv_nxt
= firstempty(tp
);
tp
->tc_flags
|= TC_ACK_DUE
;
/* THIS CODE CANT POSSIBLY WORK */
/* if any room remaining in rcv buf, take any unprocessed
messages and schedule for later processing */
while ((m
= tp
->t_rcv_unack
) != NULL
&& i
> 0) {
/* schedule work request */
t
= (struct th
*)((int)m
+ m
->m_off
);
j
= (t
->t_off
<< 2) + sizeof(struct ip
);
tp
->t_rcv_unack
= m
->m_act
;
m
->m_act
= (struct mbuf
*)0;
tcp_work(INRECV
, 0, tp
, t
);
/* remaining buffer space */
for (n
= m
; n
!= NULL
; n
= n
->m_next
)
register struct mbuf
*m
, **mp
;
/* connection must be synced and data available for user */
if (((tp
->tc_flags
&TC_SYN_ACKED
) == 0) ||
(t
= tp
->t_rcv_next
) == (struct th
*)tp
)
ready
= firstempty(tp
); /* seq # of last complete datum */
while (up
->uc_rsize
< up
->uc_rcv
&& t
!= (struct th
*) tp
&&
* Let user know about foreign tcp close if no more data.
if ((tp
->tc_flags
&TC_FIN_RCVD
) && (tp
->tc_flags
&TC_USR_CLOSED
) == 0 &&