/* tcp_input.c 1.22 81/11/14 */
#include "../h/socketvar.h"
#include "../net/inet_cksum.h"
#include "../net/inet_pcb.h"
#include "../net/inet_systm.h"
#include "../net/inet_host.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
)
register struct mbuf
*mp
;
register struct tcpiphdr
*n
; /* known to be r10 */
register struct tcpcb
*tp
;
* Build extended tcp header
n
= mtod(mp
, struct tcpiphdr
*);
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
CKSUM_TCPCHK(mp
, n
, r10
, sizeof (struct ip
) + tlen
);
inp
= in_pcblookup(&tcb
, &n
->t_s
, fport
, &n_lhost
, lport
);
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 ((thflags
&TH_ACK
) || !syn_ok(tp
, n
)) {
if (!ack_ok(tp
, n
) || !syn_ok(tp
, n
)) {
tcp_sndrst(tp
, n
); /* 71,72,75 */
tcp_error(tp
, ENETRESET
);
if ((thflags
&TH_RST
) == 0)
if (n
->t_seq
< tp
->rcv_nxt
) /* bad rst */
tcp_error(tp
, ENETRESET
);
if (ack_ok(tp
, n
) == 0) {
tcp_sndrst(tp
, n
); /* 74 */
if (syn_ok(tp
, n
) && n
->t_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
&&
n
->t_len
!= 0 && mbstat
.m_bufs
< mbstat
.m_lowat
) {
mp->m_act = (struct mbuf *)0;
if ((m = tp->seg_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_socket
->so_options
& SO_DEBUG
) || tcpconsdebug
) {
tdb_setup(tp
, n
, INRECV
, &tdb
);
((inp
->inp_lhost
= in_hmake(&n
->t_s
)) == 0)) {
inp
->inp_fport
= n
->t_src
;
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 ((thflags
&TH_ACK
) == 0) {
tp
->t_finack
= T_2ML
; /* 9 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
nstate
= (thflags
&TH_ACK
) ? ESTAB
: SYN_RCVD
; /* 11:8 */
if ((thflags
&TH_ACK
) == 0 ||
(thflags
&TH_ACK
) && n
->t_ackno
<= tp
->iss
) {
tcp_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
: CLOSING
; /* 28:26 */
if (tp
->tc_flags
&TC_FIN_RCVD
) {
tp
->t_finack
= T_2ML
; /* 29 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
n
->t_ackno
<= tp
->seq_fin
) {
tcp_ctldat(tp
, n
, 0); /* 30 */
tp
->tc_flags
&= ~TC_WAITED_2_ML
;
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 */
if ((thflags
&TH_FIN
) && (thflags
&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)
/* inline expansion of m_freem */
* Unwanted packed; free everything
* but the header and return an rst.
mp
->m_len
= sizeof(struct tcpiphdr
);
#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
= ((thflags
& TH_ACK
) ? 0 : TH_ACK
) | TH_RST
;
n
->t_len
= htons(TCPSIZE
);
n
->t_sum
= inet_cksum(mp
, sizeof(struct tcpiphdr
));
((struct ip
*)n
)->ip_len
= sizeof(struct tcpiphdr
);
tcp_ctldat(tp
, n0
, dataok
)
register struct tcpcb
*tp
;
register struct tcpiphdr
*n
= n0
;
register int thflags
= n
->th_flags
;
struct socket
*so
= tp
->t_inpcb
->inp_socket
;
seq_t past
= n
->t_seq
+ n
->t_len
;
urgent
= n
->t_seq
+ n
->t_urp
;
tp
->tc_flags
&= ~(TC_ACK_DUE
|TC_NEW_WINDOW
);
if ((tp
->tc_flags
&TC_SYN_RCVD
) == 0 && (thflags
&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 ((thflags
&TH_ACK
) && (tp
->tc_flags
&TC_SYN_RCVD
) &&
n
->t_ackno
> tp
->snd_una
) {
register struct mbuf
*mn
;
* Reflect newly acknowledged data.
tp
->snd_una
= n
->t_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
, 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
) && n
->t_seq
>= tp
->snd_wl
) {
tp
->tc_flags
|= TC_NEW_WINDOW
;
if (dataok
&& n
->t_len
) {
register struct tcpiphdr
*p
, *q
;
for (m
= dtom(n
); 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(n
->t_seq
, tp
->rcv_nxt
)) {
register int i
= tp
->rcv_nxt
- n
->t_seq
;
* Find a segment which begins after this one does.
for (q
= tp
->seg_next
; q
!= (struct tcpiphdr
*)tp
;
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 tcpiphdr
*)tp
) {
/* conversion to int (in i) handles seq wraparound */
q
->t_prev
->t_seq
+ q
->t_prev
->t_len
- n
->t_seq
;
/* w/o setting TC_NET_KEEP */
* While we overlap succeeding segments trim them or,
* if they are completely covered, dequeue them.
while (q
!= (struct tcpiphdr
*)tp
&&
SEQ_GT(n
->t_seq
+ n
->t_len
, q
->t_seq
)) {
register int 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.
(so
->so_rcv
.sb_cc
/*XXX+tp->rcv_seqcnt*/) - so
->so_rcv
.sb_hiwat
;
register int i
= MIN(q
->t_len
, overage
);
panic("tcp_text dropall");
* Advance rcv_next through newly completed sequence space.
while (n
->t_seq
== tp
->rcv_nxt
) {
if (n
== (struct tcpiphdr
*)tp
)
if (SEQ_GT(urgent
, tp
->rcv_urp
))
tp
->tc_flags
|= (TC_ACK_DUE
|TC_NET_KEEP
);
if ((thflags
&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 (n
!= (struct tcpiphdr
*)tp
&& n
->t_seq
< tp
->rcv_nxt
) {
sbappend(so
->so_rcv
, dtom(n
));
panic("tcp_input present");