/* tcp_input.c 1.7 81/10/29 */
#include "../inet/inet.h"
#include "../inet/inet_systm.h"
#include "../inet/inet_host.h"
#include "../inet/tcp_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
= 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
= 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 (up
->uc_rcc
> up
->uc_rhiwat
&&
&& n
->t_len
!= 0 && mbstat
.m_bufs
< mbstat
.m_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_setup(tp
, n
, INRECV
, &tdb
);
((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,
/* 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
;
* Discard duplicate data already passed to user.
if (SEQ_LT(n
->t_seq
, tp
->rcv_nxt
)) {
i
= tp
->rcv_nxt
- n
->t_seq
;
* Find a segment which begins after this one does.
for (q
= tp
->t_rcv_next
; q
!= (struct th
*)tp
; q
= q
->t_next
)
if (SEQ_GT(q
->t_seq
, n
->t_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 (q
->t_prev
!= (struct th
*)tp
) {
/* conversion to int (in i) handles seq wraparound */
i
= q
->t_prev
->t_seq
+ q
->t_prev
->t_len
- n
->t_seq
;
* While we overlap succeeding segments trim them or,
* if they are completely covered, dequeue them.
while (q
!= (struct th
*)tp
&& SEQ_GT(n
->t_seq
+ n
->t_len
, q
->t_seq
)) {
i
= (n
->t_seq
+ n
->t_len
) - q
->t_seq
;
m_freem(dtom(q
->t_prev
));
* Stick new segment in its place.
* Calculate available space and discard segments for
* which there is too much.
(tp
->t_socket
->uc_rcc
+ tp
->rcv_seqcnt
) - tp
->t_socket
->uc_rhiwat
;
i
= MIN(q
->t_len
, overage
);
tp
->tc_flags
|= TC_DROPPED_TXT
;
panic("tcp_text dropall");
* Advance rcv_next through
* newly completed sequence space
* and return forcing an ack.
while (n
->t_seq
== tp
->rcv_nxt
) {
/* present data belongs here */
if (n
== (struct th
*)tp
)
tp
->tc_flags
|= (TC_ACK_DUE
|TC_NET_KEEP
);
/* don't set TC_NET_KEEP, so that mbuf's will get dropped */
#define socket ucb /* ### */
#define t_socket t_ucb /* ### */
register struct socket
*up
;
register struct mbuf
*m
, **mp
;
/* connection must be synced and data available for user */
if ((tp
->tc_flags
&TC_SYN_ACKED
) == 0)
/* SHOULD PACK DATA IN HERE */
while (t
!= (struct th
*)tp
&& t
->t_seq
< tp
->rcv_nxt
) {
if (tp
->seqcnt
< 0) panic("present_data");
if ((tp
->tc_flags
&TC_FIN_RCVD
) && /* ### */
(tp
->tc_flags
&TC_USR_CLOSED
) == 0 && /* ### */
to_user(up
, UCLOSED
); /* ### */