/* tcp_usrreq.c 1.26 81/11/08 */
#include "../h/socketvar.h"
#include "../h/protosw.h"
#include "../net/inet_systm.h"
#include "../net/tcp_fsm.h"
#include "../net/tcp_var.h"
#include "/usr/include/errno.h"
struct tcb
*tcp_attach();
tcb
.tcb_next
= tcb
.tcb_prev
= (struct tcb
*)&tcb
;
* Tcp finite state machine entries for timer and user generated
* requests. These routines raise the ipl to that of the network
* to prevent reentry. In particluar, this requires that the software
* clock interrupt have lower priority than the network so that
* we can enter the network from timeout routines without improperly
* nesting the interrupt stack.
* Tcp protocol timeout routine called every 500 ms.
* Updates the timers in all active tcb's and
* causes finite state machine actions if timers expire.
* Search through tcb's and update active timers.
for (; tp
!= (struct tcb
*)&tcb
; tp
= tp
->tcb_hd
.tcb_next
) {
for (i
= 0; i
< TNTIMERS
; i
++) {
tcp_usrreq(tp
->t_socket
, PRU_SLOWTIMO
, 0, i
);
tcp_iss
+= ISSINCR
/2; /* increment iss */
* Cancel all timers for tcp tp.
register short *tmp
= &tp
->t_init
;
for (i
= 0; i
< TNTIMERS
; i
++)
* Process a TCP user request for tcp tb. If this is a send request
* then m is the mbuf chain of send data. If this is a timer expiration
* (called from the software clock routine), then timertype tells which timer.
tcp_usrreq(so
, req
, m
, addr
)
register struct tcb
*tp
= (struct tcb
*)so
->so_pcb
;
tp
->tc_flags
&= ~TC_NET_KEEP
;
if (tp
&& ((tp
->t_socket
->so_options
& SO_DEBUG
) || tcpconsdebug
)) {
tdb_setup(tp
, (struct th
*)0, req
, &tdb
);
* Attach a tcp control block to this socket.
* TCP is not multicast, so this is possible
* only if no connection currently exists.
tp
= tcp_attach(so
, &error
);
* Detach the TCP from this socket. This
* is possible only if a connection currently exists.
* Form connection: send a SYN.
if (nstate
!= 0 && nstate
!= CLOSED
)
* Declare no further transmissions.
* Can be generated by a user ioctl (half-close),
* or when higher level close occurs, if a close hasn't happened
* If we are aborting out of a listener or a active
* connection which has not yet completed we can just
* If we have gotten as far as receiving a syn from
* our foreign peer, we must be sure to send a FIN.
* If we have gotten a FIN from the foreign peer already
* (CLOSE_WAIT state), then all that remains is to wait
* for his ack of the FIN (LAST_ACK state). If we have
* not gotten a FIN from the foreign peer then we need
* 1. rcv ack of our FIN (to FIN_W2) and then
* send an ACK (to TIME_WAIT) and timeout at 2*MSL.
* or 2. receive hist FIN (to CLOSING), send an ACK
* (to TIME_WAIT), and then timeout.
* In any case this starts with a transition to FIN_W1 here.
tp
->tc_flags
|= TC_SND_FIN
;
tp
->tc_flags
|= TC_USR_CLOSED
;
nstate
= nstate
!= CLOSE_WAIT
? FIN_W1
: LAST_ACK
;
* In these states the user has already closed;
* trying to close again is an error.
* User notification of more window availability after
* reading out data. This should not happen before a connection
* is established or after it is closed.
* If the foreign peer has closed and the local entity
* has not, inform him of the FIN (give end of file).
* If the local entity is in RCV_WAIT state (draining data
* out of the TCP buffers after foreign close) and there
* is no more data, institute a close.
if (nstate
< ESTAB
|| nstate
== CLOSED
)
if ((tp
->tc_flags
&TC_FIN_RCVD
) &&
(tp
->tc_flags
&TC_USR_CLOSED
) == 0 &&
tcp_error(tp
, ESHUTDOWN
);
if (nstate
== RCV_WAIT
&& rcv_empty(tp
))
* Send request on open connection.
* Should not happen if the connection is not yet established.
* Allowed only on ESTAB connection and after FIN from
nstate
= tcp_usrsend(tp
, m
);
/* tcp_user(tp, UCLSERR); */
* User abort of connection.
* If a SYN has been received, but we have not exchanged FINs
* then we need to send an RST. In any case we then
if (nstate
== 0 || nstate
== CLOSED
)
tp
->tc_flags
|= TC_SND_RST
;
* Network down entry. Discard the tcb and force
* the state to be closed, ungracefully.
if (nstate
== 0 || nstate
== CLOSED
)
* Timers should expire only on open connections
nstate
= tcp_timers(tp
, (int)addr
);
printf("tcp: bad state: tcb=%x state=%d input=%d\n",
* Open routine, called to initialize newly created tcb fields.
register struct socket
*so
;
* Link in tcb queue and make
* initialize empty reassembly queue.
tp
->tcb_hd
.tcb_next
= tcb
.tcb_next
;
tcb
.tcb_next
->tcb_hd
.tcb_prev
= tp
;
tp
->tcb_hd
.tcb_prev
= (struct tcb
*)&tcb
;
tp
->tcb_hd
.seg_next
= tp
->tcb_hd
.seg_prev
= (struct th
*)tp
;
* Initialize sequence numbers and
* round trip retransmit timer.
* (Other fields were init'd to zero when tcb allocated.)
tp
->snd_end
= tp
->seq_fin
= tp
->snd_nxt
= tp
->snd_hi
= tp
->snd_una
=
tp
->snd_off
= tp
->iss
+ 1;
tcp_iss
+= (ISSINCR
>> 1) + 1;
register struct socket
*so
= tp
->t_socket
;
* Remove from tcb queue and cancel timers.
tp
->tcb_hd
.tcb_prev
->tcb_hd
.tcb_next
= tp
->tcb_hd
.tcb_next
;
tp
->tcb_hd
.tcb_next
->tcb_hd
.tcb_prev
= tp
->tcb_hd
.tcb_prev
;
for (t
= tp
->tcb_hd
.seg_next
; t
!= (struct th
*)tp
; t
= t
->t_next
)
{ m_freem(so
->so_rcv
.sb_mb
); so
->so_rcv
.sb_mb
= 0; }
so
->so_rcv
.sb_cc
= 0; so
->so_rcv
.sb_mbcnt
= 0;
{ m_freem(so
->so_snd
.sb_mb
); so
->so_rcv
.sb_mb
= 0; }
so
->so_snd
.sb_cc
= 0; so
->so_snd
.sb_mbcnt
= 0;
for (m
= tp
->seg_unack
; m
; m
= m
->m_act
)
* Free routing table entry.
* Free tcp send template, the tcb itself,
* and the space we had reserved in the meory pool.
m_free(dtom(tp
->t_template
));
wmemfree((caddr_t
)tp
, 1024);
m_release(so
->so_rcv
.sb_hiwat
+ so
->so_snd
.sb_hiwat
+ 2 * MSIZE
);
* Send data queue headed by m0 into the protocol.
register struct mbuf
*m
, *n
;
register struct socket
*so
= tp
->t_socket
;
for (m
= n
= m0
; m
!= NULL
; m
= m
->m_next
) {
so
->so_snd
.sb_mbcnt
+= NMBPG
;
if ((m
= so
->so_snd
.sb_mb
) == NULL
)
while (m
->m_next
!= NULL
) {
if (m
->m_off
<= MMAXOFF
) {
off
= m
->m_off
+ m
->m_len
;
while (n
&& n
->m_off
<= MMAXOFF
&&
(MMAXOFF
- off
) >= n
->m_len
) {
bcopy((caddr_t
)((int)n
+ n
->m_off
),
(caddr_t
)((int)m
+ off
), n
->m_len
);
if (tp
->t_options
& TO_EOL
)
if (tp
->t_options
& TO_URG
) {
tp
->tc_flags
|= TC_SND_URG
;
* TCP timer went off processing.
tcp_timers(tp
, timertype
)
case TINIT
: /* initialization timer */
if ((tp
->tc_flags
&TC_SYN_ACKED
) == 0) { /* 35 */
/* XXX */ /* tcp_close(tp, UINTIMO); */
case TFINACK
: /* fin-ack timer */
* We can be sure our ACK of foreign FIN was rcvd,
* and can close if no data left for user.
/* XXX */ /* tcp_close(tp, UCLOSED); */ /* 14 */
return (RCV_WAIT
); /* 17 */
tp
->tc_flags
|= TC_WAITED_2_ML
;
case TREXMT
: /* retransmission timer */
if (tp
->t_rexmt_val
> tp
->snd_una
) { /* 34 */
* Set so for a retransmission, increase rexmt time
* in case of multiple retransmissions.
tp
->snd_nxt
= tp
->snd_una
;
tp
->tc_flags
|= TC_REXMT
;
tp
->t_xmtime
= tp
->t_xmtime
<< 1;
if (tp
->t_xmtime
> T_REMAX
)
case TREXMTTL
: /* retransmit too long */
if (tp
->t_rtl_val
> tp
->snd_una
) /* 36 */
/* XXX */ /* to_user(tp->t_socket, URXTIMO); */;
* If user has already closed, abort the connection.
if (tp
->tc_flags
& TC_USR_CLOSED
) {
/* XXX */ /* tcp_close(tp, URXTIMO); */
case TPERSIST
: /* persist timer */
* Force a byte send through closed window.
tp
->tc_flags
|= TC_FORCE_ONE
;
* TCP debugging utility subroutines.
* THE NAMES OF THE FIELDS USED BY THESE ROUTINES ARE STUPID.
tdb_setup(tp
, n
, input
, tdp
)
register struct tcp_debug
*tdp
;
tdp
->td_old
= tp
->t_state
;
tdp
->td_ano
= n
->t_ackno
;
tdp
->td_flg
= n
->th_flags
;
tdp
->td_sno
= tdp
->td_ano
= tdp
->td_wno
= tdp
->td_lno
=
tcp_debug
[tdbx
++ % TDBSIZE
] = *tdp
;
register struct tcp_debug
*tdp
;
printf("%x ", ((int)tdp
->td_tcb
)&0xffffff);
if (tdp
->td_inp
== INSEND
) {
printf("SEND #%x", tdp
->td_sno
);
tdp
->td_lno
= ntohs(tdp
->td_lno
);
tdp
->td_wno
= ntohs(tdp
->td_wno
);
if (tdp
->td_inp
== INRECV
)
printf("RCV #%x ", tdp
->td_sno
);
tcpstates
[tdp
->td_old
], tcpinputs
[tdp
->td_inp
]);
if (tdp
->td_inp
== ISTIMER
)
printf("(%s)", tcptimers
[tdp
->td_tim
]);
tcpstates
[(tdp
->td_new
> 0) ? tdp
->td_new
: tdp
->td_old
]);
/* GROSS... DEPENDS ON SIGN EXTENSION OF CHARACTERS */
printf(" len=%d", tdp
->td_lno
);
printf(" win=%d", tdp
->td_wno
);
if (tdp
->td_flg
& TH_FIN
) printf(" FIN");
if (tdp
->td_flg
& TH_SYN
) printf(" SYN");
if (tdp
->td_flg
& TH_RST
) printf(" RST");
if (tdp
->td_flg
& TH_EOL
) printf(" EOL");
if (tdp
->td_flg
& TH_ACK
) printf(" ACK %x", tdp
->td_ano
);
if (tdp
->td_flg
& TH_URG
) printf(" URG");