e6fbd69c27a9e4856c4cec07088dcff98150002a
/* tcp_usrreq.c 1.25 81/11/04 */
#include "../inet/inet.h"
#include "../inet/inet_systm.h"
#include "../inet/tcp_fsm.h"
* 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 once per second.
* 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
= tcb
.tcb_next
; tp
!= (struct tcb
*)&tcb
; tp
= tp
->tcb_next
) {
for (i
= 0; i
< TNTIMERS
; i
++) {
tcp_usrreq(ISTIMER
, i
, tp
, 0);
tcp_iss
+= ISSINCR
; /* increment iss */
timeout(tcp_timeo
, 0, hz
); /* reschedule every second */
* Cancel all timers for tcp tp.
register u_char
*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(input
, timertype
, tp
, m
)
tp
->tc_flags
&= ~TC_NET_KEEP
;
acounts
[nstate
][input
]++;
if ((tp
->t_ucb
->uc_flags
& UDEBUG
) || tcpconsdebug
) {
tdb_setup(tp
, (struct th
*)0, input
, &tdb
);
* Passive open. Create a tcp control block
* and enter listen state.
if (nstate
!= 0 && nstate
!= CLOSED
)
* Active open. Create a tcp control block,
* send a SYN and enter SYN_SENT state.
if (nstate
!= 0 && nstate
!= CLOSED
)
* Tcp close call. 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.
to_user(tp
->t_ucb
, UCLSERR
);
* Timers should expire only on open connections
nstate
= tcp_timers(tp
, timertype
);
* 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 &&
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
);
* 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
)
printf("tcp: bad state: tcb=%x state=%d input=%d\n",
* Open routine, called to initialize newly created tcb fields.
register struct ucb
*up
= tp
->t_ucb
;
* Link in tcb queue and make
* initialize empty reassembly queue.
tp
->tcb_next
= tcb
.tcb_next
;
tcb
.tcb_next
->tcb_prev
= tp
;
tp
->tcb_prev
= (struct tcb
*)&tcb
;
tp
->t_rcv_next
= tp
->t_rcv_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;
* SHOULD THIS BE A HIGHER LEVEL FUNCTION!?! THINK SO.
tp
->t_init
= up
->uc_timeo
;
up
->uc_timeo
= 0; /* ### */
* Internal close of a connection, shutting down the tcb.
register struct ucb
*up
= tp
->t_ucb
;
* Remove from tcb queue and cancel timers.
tp
->tcb_prev
->tcb_next
= tp
->tcb_next
;
tp
->tcb_next
->tcb_prev
= tp
->tcb_prev
;
for (t
= tp
->t_rcv_next
; t
!= (struct th
*)tp
; t
= t
->t_next
)
for (m
= tp
->t_rcv_unack
; m
!= NULL
; m
= m
->m_act
) {
* Free tcp send template, the tcb itself,
* the routing table entry, and the space we had reserved
m_free(dtom(tp
->t_template
));
wmemfree((caddr_t
)tp
, 1024);
m_release(up
->uc_snd
+ (up
->uc_rhiwat
/MSIZE
) + 2);
* If user has initiated close (via close call), delete ucb
* entry, otherwise just wakeup so user can issue close call
if (tp
->tc_flags
&TC_USR_ABORT
) /* ### */
up
->uc_proc
= NULL
; /* ### */
to_user(up
, state
); /* ### */
* Send data queue headed by m0 into the protocol.
register struct mbuf
*m
, *n
;
register struct ucb
*up
= tp
->t_ucb
;
for (m
= n
= m0
; m
!= NULL
; m
= m
->m_next
) {
if ((m
= up
->uc_sbuf
) == 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 (up
->uc_flags
& UURG
) {
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 */
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.
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 up 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 */
to_user(tp
->t_ucb
, URXTIMO
);
* If user has already closed, abort the connection.
if (tp
->tc_flags
& TC_USR_CLOSED
) {
case TPERSIST
: /* persist timer */
* Force a byte send through closed window.
tp
->tc_flags
|= TC_FORCE_ONE
;
/* THIS ROUTINE IS A CROCK */
psignal(up
->uc_proc
, SIGURG
);
* 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");