diff options
Diffstat (limited to 'sys/netiso/tp.trans')
-rw-r--r-- | sys/netiso/tp.trans | 1342 |
1 files changed, 1342 insertions, 0 deletions
diff --git a/sys/netiso/tp.trans b/sys/netiso/tp.trans new file mode 100644 index 0000000..edefc76 --- /dev/null +++ b/sys/netiso/tp.trans @@ -0,0 +1,1342 @@ +/* NEW */ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tp.trans 8.1 (Berkeley) 6/10/93 + */ + +/*********************************************************** + Copyright IBM Corporation 1987 + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of IBM not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* + * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison + */ +/* $Header: tp.trans,v 5.1 88/10/12 12:22:07 root Exp $ + * + * Transition file for TP. + * + * DO NOT: + * - change the order of any of the events or states. to do so will + * make tppt, netstat, etc. cease working. + * + * NOTE: + * some hooks exist for data on (dis)connect, but it's ***NOT***SUPPORTED*** + * (read: may not work!) + * + * I tried to put everything that causes a change of state in here, hence + * there are some seemingly trivial events like T_DETACH and T_LISTEN_req. + * + * Almost everything having to do w/ setting & cancelling timers is here + * but once it was debugged, I moved the setting of the + * keepalive (sendack) timer to tp_emit(), where an AK_TPDU is sent. + * This is so the code wouldn't be duplicated all over creation in here. + * + */ +*PROTOCOL tp + +*INCLUDE +{ +/* @(#)tp.trans 8.1 (Berkeley) 6/10/93 */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/protosw.h> +#include <sys/mbuf.h> +#include <sys/time.h> +#include <sys/errno.h> + +#include <netiso/tp_param.h> +#include <netiso/tp_stat.h> +#include <netiso/tp_pcb.h> +#include <netiso/tp_tpdu.h> +#include <netiso/argo_debug.h> +#include <netiso/tp_trace.h> +#include <netiso/iso_errno.h> +#include <netiso/tp_seq.h> +#include <netiso/cons.h> + +#define DRIVERTRACE TPPTdriver +#define sbwakeup(sb) sowakeup(p->tp_sock, sb); +#define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0) + +static trick_hc = 1; + +int tp_emit(), + tp_goodack(), tp_goodXack(), + tp_stash() +; +void tp_indicate(), tp_getoptions(), + tp_soisdisconnecting(), tp_soisdisconnected(), + tp_recycle_tsuffix(), +#ifdef TP_DEBUG_TIMERS + tp_etimeout(), tp_euntimeout(), + tp_ctimeout(), tp_cuntimeout(), + tp_ctimeout_MIN(), +#endif + tp_freeref(), tp_detach(), + tp0_stash(), tp0_send(), + tp_netcmd(), tp_send() +; + +typedef struct tp_pcb tpcb_struct; + + +} + +*PCB tpcb_struct SYNONYM P + +*STATES + +TP_CLOSED +TP_CRSENT +TP_AKWAIT +TP_OPEN +TP_CLOSING +TP_REFWAIT +TP_LISTENING /* Local to this implementation */ +TP_CONFIRMING /* Local to this implementation */ + +*EVENTS { struct timeval e_time; } SYNONYM E + + /* + * C (typically cancelled) timers - + * + * let these be the first ones so for the sake of convenience + * their values are 0--> n-1 + * DO NOT CHANGE THE ORDER OF THESE TIMER EVENTS!! + */ + TM_inact + TM_retrans + /* TM_retrans is used for all + * simple retransmissions - CR,CC,XPD,DR + */ + + TM_sendack + /* TM_sendack does dual duty - keepalive AND closed-window + * Probes. + * It's set w/ keepalive-ticks every time an ack is sent. + * (this is done in (void) tp_emit() ). + * Whenever a DT arrives which doesn't require immediate acking, + * a separate fast-timeout flag is set ensuring 200ms response. + */ + TM_notused + + /* + * E (typically expired) timers - these may be in any order. + * These cause procedures to be executed directly; may not + * cause an 'event' as we know them here. + */ + TM_reference { SeqNum e_low; SeqNum e_high; int e_retrans; } + TM_data_retrans { SeqNum e_low; SeqNum e_high; int e_retrans; } + +/* NOTE: in tp_input is a minor optimization that assumes that + * for all tpdu types that can take e_data and e_datalen, these + * fields fall in the same place in the event structure, that is, + * e_data is the first field and e_datalen is the 2nd field. + */ + + ER_TPDU { + u_char e_reason; + } + CR_TPDU { struct mbuf *e_data; /* first field */ + int e_datalen; /* 2nd field */ + u_int e_cdt; + } + DR_TPDU { struct mbuf *e_data; /* first field */ + int e_datalen; /* 2nd field */ + u_short e_sref; + u_char e_reason; + } + DC_TPDU + CC_TPDU { struct mbuf *e_data; /* first field */ + int e_datalen; /* 2nd field */ + u_short e_sref; + u_int e_cdt; + } + AK_TPDU { u_int e_cdt; + SeqNum e_seq; + SeqNum e_subseq; + u_char e_fcc_present; + } + DT_TPDU { struct mbuf *e_data; /* first field */ + int e_datalen; /* 2nd field */ + u_int e_eot; + SeqNum e_seq; + } + XPD_TPDU { struct mbuf *e_data; /* first field */ + int e_datalen; /* 2nd field */ + SeqNum e_seq; + } + XAK_TPDU { SeqNum e_seq; } + + T_CONN_req + T_DISC_req { u_char e_reason; } + T_LISTEN_req + T_DATA_req + T_XPD_req + T_USR_rcvd + T_USR_Xrcvd + T_DETACH + T_NETRESET + T_ACPT_req + + +*TRANSITIONS + + +/* TP_AKWAIT doesn't exist in TP 0 */ +SAME <== TP_AKWAIT [ CC_TPDU, DC_TPDU, XAK_TPDU ] + DEFAULT + NULLACTION +; + + +/* applicable in TP4, TP0 */ +SAME <== TP_REFWAIT DR_TPDU + ( $$.e_sref != 0 ) + { + (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); + } +; + +/* applicable in TP4, TP0 */ +SAME <== TP_REFWAIT [ CR_TPDU, CC_TPDU, DT_TPDU, + DR_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU, DC_TPDU, ER_TPDU ] + DEFAULT + { +# ifdef TP_DEBUG + if( $E.ev_number != AK_TPDU ) + printf("TPDU 0x%x in REFWAIT!!!!\n", $E.ev_number); +# endif TP_DEBUG + } +; + +/* applicable in TP4, TP0 */ +SAME <== TP_REFWAIT [ T_DETACH, T_DISC_req ] + DEFAULT + NULLACTION +; + +/* applicable in TP4, TP0 */ +SAME <== TP_CRSENT AK_TPDU + ($P.tp_class == TP_CLASS_0) + { + /* oh, man is this grotesque or what? */ + (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq); + /* but it's necessary because this pseudo-ack may happen + * before the CC arrives, but we HAVE to adjust the + * snduna as a result of the ack, WHENEVER it arrives + */ + } +; + +/* applicable in TP4, TP0 */ +SAME <== TP_CRSENT + [ CR_TPDU, DC_TPDU, DT_TPDU, XPD_TPDU, XAK_TPDU ] + DEFAULT + NULLACTION +; + +/* applicable in TP4, TP0 */ +SAME <== TP_CLOSED [ DT_TPDU, XPD_TPDU, + ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ] + DEFAULT + NULLACTION +; + +/* TP_CLOSING doesn't exist in TP 0 */ +SAME <== TP_CLOSING + [ CC_TPDU, CR_TPDU, DT_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU ] + DEFAULT + NULLACTION +; + + +/* DC_TPDU doesn't exist in TP 0 */ +SAME <== TP_OPEN DC_TPDU + DEFAULT + NULLACTION +; + +/* applicable in TP4, TP0 */ +SAME <== TP_LISTENING [DR_TPDU, CC_TPDU, DT_TPDU, XPD_TPDU, + ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ] + DEFAULT + NULLACTION +; + +/* applicable in TP4, TP0 */ +TP_LISTENING <== TP_CLOSED T_LISTEN_req + DEFAULT + NULLACTION +; + +/* applicable in TP4, TP0 */ +TP_CLOSED <== [ TP_LISTENING, TP_CLOSED ] T_DETACH + DEFAULT + { + tp_detach($P); + } +; + +TP_CONFIRMING <== TP_LISTENING CR_TPDU + ( $P.tp_class == TP_CLASS_0) + { + $P.tp_refstate = REF_OPEN; /* has timers ??? */ + } +; + +TP_CONFIRMING <== TP_LISTENING CR_TPDU + DEFAULT + { + IFTRACE(D_CONN) + tptrace(TPPTmisc, "CR datalen data", $$.e_datalen, $$.e_data,0,0); + ENDTRACE + IFDEBUG(D_CONN) + printf("CR datalen 0x%x data 0x%x", $$.e_datalen, $$.e_data); + ENDDEBUG + $P.tp_refstate = REF_OPEN; /* has timers */ + $P.tp_fcredit = $$.e_cdt; + + if ($$.e_datalen > 0) { + /* n/a for class 0 */ + ASSERT($P.tp_Xrcv.sb_cc == 0); + sbappendrecord(&$P.tp_Xrcv, $$.e_data); + $$.e_data = MNULL; + } + } +; + +TP_OPEN <== TP_CONFIRMING T_ACPT_req + ( $P.tp_class == TP_CLASS_0 ) + { + IncStat(ts_tp0_conn); + IFTRACE(D_CONN) + tptrace(TPPTmisc, "Confiming", $P, 0,0,0); + ENDTRACE + IFDEBUG(D_CONN) + printf("Confirming connection: $P" ); + ENDDEBUG + soisconnected($P.tp_sock); + (void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ; + $P.tp_fcredit = 1; + } +; + +TP_AKWAIT <== TP_CONFIRMING T_ACPT_req + (tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0) + { + IncStat(ts_tp4_conn); /* even though not quite open */ + IFTRACE(D_CONN) + tptrace(TPPTmisc, "Confiming", $P, 0,0,0); + ENDTRACE + IFDEBUG(D_CONN) + printf("Confirming connection: $P" ); + ENDDEBUG + tp_getoptions($P); + soisconnecting($P.tp_sock); + if (($P.tp_rx_strat & TPRX_FASTSTART) && ($P.tp_fcredit > 0)) + $P.tp_cong_win = $P.tp_fcredit * $P.tp_l_tpdusize; + $P.tp_retrans = $P.tp_Nretrans; + tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks); + } +; + +/* TP4 only */ +TP_CLOSED <== TP_CONFIRMING T_ACPT_req + DEFAULT /* emit failed */ + { + IFDEBUG(D_CONN) + printf("event: CR_TPDU emit CC failed done " ); + ENDDEBUG + soisdisconnected($P.tp_sock); + tp_recycle_tsuffix($P); + tp_freeref($P.tp_lref); + tp_detach($P); + } +; + +/* applicable in TP4, TP0 */ +TP_CRSENT <== TP_CLOSED T_CONN_req + DEFAULT + { + int error; + struct mbuf *data = MNULL; + + IFTRACE(D_CONN) + tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags, + $P.tp_ucddata, 0, 0); + ENDTRACE + data = MCPY($P.tp_ucddata, M_WAIT); + if (data) { + IFDEBUG(D_CONN) + printf("T_CONN_req.trans m_copy cc 0x%x\n", + $P.tp_ucddata); + dump_mbuf(data, "sosnd @ T_CONN_req"); + ENDDEBUG + } + + if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) + return error; /* driver WON'T change state; will return error */ + + $P.tp_refstate = REF_OPEN; /* has timers */ + if($P.tp_class != TP_CLASS_0) { + $P.tp_retrans = $P.tp_Nretrans; + tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks); + } + } +; + +/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */ +TP_REFWAIT <== [ TP_CRSENT, TP_AKWAIT, TP_OPEN ] DR_TPDU + DEFAULT + { + sbflush(&$P.tp_Xrcv); /* purge non-delivered data data */ + if ($$.e_datalen > 0) { + sbappendrecord(&$P.tp_Xrcv, $$.e_data); + $$.e_data = MNULL; + } + if ($P.tp_state == TP_OPEN) + tp_indicate(T_DISCONNECT, $P, 0); + else { + int so_error = ECONNREFUSED; + if ($$.e_reason != (E_TP_NO_SESSION ^ TP_ERROR_MASK) && + $$.e_reason != (E_TP_NO_CR_ON_NC ^ TP_ERROR_MASK) && + $$.e_reason != (E_TP_REF_OVERFLOW ^ TP_ERROR_MASK)) + so_error = ECONNABORTED; + tp_indicate(T_DISCONNECT, $P, so_error); + } + tp_soisdisconnected($P); + if ($P.tp_class != TP_CLASS_0) { + if ($P.tp_state == TP_OPEN ) { + tp_euntimeout($P, TM_data_retrans); /* all */ + tp_cuntimeout($P, TM_retrans); + tp_cuntimeout($P, TM_inact); + tp_cuntimeout($P, TM_sendack); + $P.tp_flags &= ~TPF_DELACK; + } + tp_cuntimeout($P, TM_retrans); + if( $$.e_sref != 0 ) + (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); + } + } +; + +SAME <== TP_CLOSED DR_TPDU + DEFAULT + { + if( $$.e_sref != 0 ) + (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); + /* reference timer already set - reset it to be safe (???) */ + tp_euntimeout($P, TM_reference); /* all */ + tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks); + } +; + +/* NBS(34) */ +TP_REFWAIT <== TP_CRSENT ER_TPDU + DEFAULT + { + tp_cuntimeout($P, TM_retrans); + tp_indicate(ER_TPDU, $P, $$.e_reason); + tp_soisdisconnected($P); + } +; + +/* NBS(27) */ +TP_REFWAIT <== TP_CLOSING DR_TPDU + DEFAULT + { + tp_cuntimeout($P, TM_retrans); + tp_soisdisconnected($P); + } +; +/* these two transitions are the same but can't be combined because xebec + * can't handle the use of $$.e_reason if they're combined + */ +/* NBS(27) */ +TP_REFWAIT <== TP_CLOSING ER_TPDU + DEFAULT + { + tp_indicate(ER_TPDU, $P, $$.e_reason); + tp_cuntimeout($P, TM_retrans); + tp_soisdisconnected($P); + } +; +/* NBS(27) */ +TP_REFWAIT <== TP_CLOSING DC_TPDU + DEFAULT + { + tp_cuntimeout($P, TM_retrans); + tp_soisdisconnected($P); + } +; + +/* NBS(21) */ +SAME <== TP_CLOSED [ CC_TPDU, CR_TPDU ] + DEFAULT + { /* don't ask me why we have to do this - spec says so */ + (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL); + /* don't bother with retransmissions of the DR */ + } +; + +/* NBS(34) */ +TP_REFWAIT <== TP_OPEN ER_TPDU + ($P.tp_class == TP_CLASS_0) + { + tp_soisdisconnecting($P.tp_sock); + tp_indicate(ER_TPDU, $P, $$.e_reason); + tp_soisdisconnected($P); + tp_netcmd( $P, CONN_CLOSE ); + } +; + +TP_CLOSING <== [ TP_AKWAIT, TP_OPEN ] ER_TPDU + DEFAULT + { + if ($P.tp_state == TP_OPEN) { + tp_euntimeout($P, TM_data_retrans); /* all */ + tp_cuntimeout($P, TM_inact); + tp_cuntimeout($P, TM_sendack); + } + tp_soisdisconnecting($P.tp_sock); + tp_indicate(ER_TPDU, $P, $$.e_reason); + $P.tp_retrans = $P.tp_Nretrans; + tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); + (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL); + } +; +/* NBS(6) */ +TP_OPEN <== TP_CRSENT CC_TPDU + ($P.tp_class == TP_CLASS_0) + { + tp_cuntimeout($P, TM_retrans); + IncStat(ts_tp0_conn); + $P.tp_fcredit = 1; + soisconnected($P.tp_sock); + } +; + +TP_OPEN <== TP_CRSENT CC_TPDU + DEFAULT + { + IFDEBUG(D_CONN) + printf("trans: CC_TPDU in CRSENT state flags 0x%x\n", + (int)$P.tp_flags); + ENDDEBUG + IncStat(ts_tp4_conn); + $P.tp_fref = $$.e_sref; + $P.tp_fcredit = $$.e_cdt; + if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0)) + $P.tp_cong_win = $$.e_cdt * $P.tp_l_tpdusize; + tp_getoptions($P); + tp_cuntimeout($P, TM_retrans); + if ($P.tp_ucddata) { + IFDEBUG(D_CONN) + printf("dropping user connect data cc 0x%x\n", + $P.tp_ucddata->m_len); + ENDDEBUG + m_freem($P.tp_ucddata); + $P.tp_ucddata = 0; + } + soisconnected($P.tp_sock); + if ($$.e_datalen > 0) { + ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */ + sbappendrecord(&$P.tp_Xrcv, $$.e_data); + $$.e_data = MNULL; + } + + (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + } +; + +/* TP4 only */ +SAME <== TP_CRSENT TM_retrans + ( $P.tp_retrans > 0 ) + { + struct mbuf *data = MNULL; + int error; + + IncStat(ts_retrans_cr); + $P.tp_cong_win = 1 * $P.tp_l_tpdusize; + data = MCPY($P.tp_ucddata, M_NOWAIT); + if($P.tp_ucddata) { + IFDEBUG(D_CONN) + printf("TM_retrans.trans m_copy cc 0x%x\n", data); + dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans"); + ENDDEBUG + if( data == MNULL ) + return ENOBUFS; + } + + $P.tp_retrans --; + if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) { + $P.tp_sock->so_error = error; + } + tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks); + } +; + +/* TP4 only */ +TP_REFWAIT <== TP_CRSENT TM_retrans + DEFAULT /* no more CR retransmissions */ + { + IncStat(ts_conn_gaveup); + $P.tp_sock->so_error = ETIMEDOUT; + tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); + tp_soisdisconnected($P); + } +; + +/* TP4 only */ +SAME <== TP_AKWAIT CR_TPDU + DEFAULT + /* duplicate CR (which doesn't really exist in the context of + * a connectionless network layer) + * Doesn't occur in class 0. + */ + { + int error; + struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); + + if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) { + $P.tp_sock->so_error = error; + } + $P.tp_retrans = $P.tp_Nretrans; + tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks); + } +; + +/* TP4 only */ +TP_OPEN <== TP_AKWAIT DT_TPDU + ( IN_RWINDOW( $P, $$.e_seq, + $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) ) + { + int doack; + + /* + * Get rid of any confirm or connect data, so that if we + * crash or close, it isn't thought of as disconnect data. + */ + if ($P.tp_ucddata) { + m_freem($P.tp_ucddata); + $P.tp_ucddata = 0; + } + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + tp_cuntimeout($P, TM_retrans); + soisconnected($P.tp_sock); + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + + /* see also next 2 transitions, if you make any changes */ + + doack = tp_stash($P, $E); + IFDEBUG(D_DATA) + printf("tp_stash returns %d\n",doack); + ENDDEBUG + + if (doack) { + (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); + tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks); + } else + tp_ctimeout( $P, TM_sendack, (int)$P.tp_sendack_ticks); + + IFDEBUG(D_DATA) + printf("after stash calling sbwakeup\n"); + ENDDEBUG + } +; + +SAME <== TP_OPEN DT_TPDU + ( $P.tp_class == TP_CLASS_0 ) + { + tp0_stash($P, $E); + sbwakeup( &$P.tp_sock->so_rcv ); + + IFDEBUG(D_DATA) + printf("after stash calling sbwakeup\n"); + ENDDEBUG + } +; + +/* TP4 only */ +SAME <== TP_OPEN DT_TPDU + ( IN_RWINDOW( $P, $$.e_seq, + $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) ) + { + int doack; /* tells if we must ack immediately */ + + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + sbwakeup( &$P.tp_sock->so_rcv ); + + doack = tp_stash($P, $E); + IFDEBUG(D_DATA) + printf("tp_stash returns %d\n",doack); + ENDDEBUG + + if(doack) + (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); + else + tp_ctimeout_MIN( $P, TM_sendack, (int)$P.tp_sendack_ticks); + + IFDEBUG(D_DATA) + printf("after stash calling sbwakeup\n"); + ENDDEBUG + } +; + +/* Not in window - we must ack under certain circumstances, namely + * a) if the seq number is below lwe but > lwe - (max credit ever given) + * (to handle lost acks) Can use max-possible-credit for this ^^^. + * and + * b) seq number is > uwe but < uwe + previously sent & withdrawn credit + * + * (see 12.2.3.8.1 of ISO spec, p. 73) + * We just always ack. + */ +/* TP4 only */ +SAME <== [ TP_OPEN, TP_AKWAIT ] DT_TPDU + DEFAULT /* Not in window */ + { + IFTRACE(D_DATA) + tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ", + $$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0); + ENDTRACE + IncStat(ts_dt_niw); + m_freem($$.e_data); + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); + } +; + +/* TP4 only */ +TP_OPEN <== TP_AKWAIT AK_TPDU + DEFAULT + { + if ($P.tp_ucddata) { + m_freem($P.tp_ucddata); + $P.tp_ucddata = 0; + } + (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq); + tp_cuntimeout($P, TM_retrans); + + soisconnected($P.tp_sock); + IFTRACE(D_CONN) + struct socket *so = $P.tp_sock; + tptrace(TPPTmisc, + "called sosiconn: so so_state rcv.sb_sel rcv.sb_flags", + so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags); + tptrace(TPPTmisc, + "called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head", + so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head); + ENDTRACE + + tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks); + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + } +; + +/* TP4 only */ +TP_OPEN <== [ TP_OPEN, TP_AKWAIT ] XPD_TPDU + ($P.tp_Xrcvnxt == $$.e_seq) + { + if( $P.tp_state == TP_AKWAIT ) { + if ($P.tp_ucddata) { + m_freem($P.tp_ucddata); + $P.tp_ucddata = 0; + } + tp_cuntimeout($P, TM_retrans); + soisconnected($P.tp_sock); + tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks); + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + } + IFTRACE(D_XPD) + tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n", + $P.tp_Xrcvnxt,$$.e_seq, $$.e_datalen, $$.e_data->m_len); + ENDTRACE + + $P.tp_sock->so_state |= SS_RCVATMARK; + $$.e_data->m_flags |= M_EOR; + sbinsertoob(&$P.tp_Xrcv, $$.e_data); + IFDEBUG(D_XPD) + dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv"); + ENDDEBUG + tp_indicate(T_XDATA, $P, 0); + sbwakeup( &$P.tp_Xrcv ); + + (void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL); + SEQ_INC($P, $P.tp_Xrcvnxt); + } +; + +/* TP4 only */ +SAME <== TP_OPEN T_USR_Xrcvd + DEFAULT + { + if( $P.tp_Xrcv.sb_cc == 0 ) { + /* kludge for select(): */ + /* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */ + } + } + /* OLD WAY: + * Ack only after the user receives the XPD. This is better for + * users that use one XPD right after another. + * Acking right away (the NEW WAY, see the prev. transition) is + * better for occasional * XPD, when the receiving user doesn't + * want to read the XPD immediately (which is session's behavior). + * + int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL); + SEQ_INC($P, $P.tp_Xrcvnxt); + return error; + */ +; + +/* NOTE: presently if the user doesn't read the connection data + * before and expedited data PDU comes in, the connection data will + * be dropped. This is a bug. To avoid it, we need somewhere else + * to put the connection data. + * On the other hand, we need not to have it sitting around forever. + * This is a problem with the idea of trying to accommodate + * data on connect w/ a passive-open user interface. + */ +/* TP4 only */ + +SAME <== [ TP_AKWAIT, TP_OPEN ] XPD_TPDU + DEFAULT /* not in window or cdt==0 */ + { + IFTRACE(D_XPD) + tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n", + $P.tp_Xrcvnxt, $$.e_seq, $P.tp_Xrcv.sb_cc , 0); + ENDTRACE + if( $P.tp_Xrcvnxt != $$.e_seq ) + IncStat(ts_xpd_niw); + if( $P.tp_Xrcv.sb_cc ) { + /* might as well kick 'em again */ + tp_indicate(T_XDATA, $P, 0); + IncStat(ts_xpd_dup); + } + m_freem($$.e_data); + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + /* don't send an xack because the xak gives "last one received", not + * "next one i expect" (dumb) + */ + } +; + +/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries + * to detach all its "children" + * Also (CRSENT) when user kills a job that's doing a connect() + */ +TP_REFWAIT <== TP_CRSENT T_DETACH + ($P.tp_class == TP_CLASS_0) + { + struct socket *so = $P.tp_sock; + + /* detach from parent socket so it can finish closing */ + if (so->so_head) { + if (!soqremque(so, 0) && !soqremque(so, 1)) + panic("tp: T_DETACH"); + so->so_head = 0; + } + tp_soisdisconnecting($P.tp_sock); + tp_netcmd( $P, CONN_CLOSE); + tp_soisdisconnected($P); + } +; + +/* TP4 only */ +TP_CLOSING <== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ] T_DETACH + DEFAULT + { + struct socket *so = $P.tp_sock; + struct mbuf *data = MNULL; + + /* detach from parent socket so it can finish closing */ + if (so->so_head) { + if (!soqremque(so, 0) && !soqremque(so, 1)) + panic("tp: T_DETACH"); + so->so_head = 0; + } + if ($P.tp_state != TP_CLOSING) { + tp_soisdisconnecting($P.tp_sock); + data = MCPY($P.tp_ucddata, M_NOWAIT); + (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data); + $P.tp_retrans = $P.tp_Nretrans; + tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); + } + } +; + +TP_REFWAIT <== [ TP_OPEN, TP_CRSENT ] T_DISC_req + ( $P.tp_class == TP_CLASS_0 ) + { + tp_soisdisconnecting($P.tp_sock); + tp_netcmd( $P, CONN_CLOSE); + tp_soisdisconnected($P); + } +; + +/* TP4 only */ +TP_CLOSING <== [ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ] T_DISC_req + DEFAULT + { + struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); + + if($P.tp_state == TP_OPEN) { + tp_euntimeout($P, TM_data_retrans); /* all */ + tp_cuntimeout($P, TM_inact); + tp_cuntimeout($P, TM_sendack); + $P.tp_flags &= ~TPF_DELACK; + } + if (data) { + IFDEBUG(D_CONN) + printf("T_DISC_req.trans tp_ucddata 0x%x\n", + $P.tp_ucddata); + dump_mbuf(data, "ucddata @ T_DISC_req"); + ENDDEBUG + } + tp_soisdisconnecting($P.tp_sock); + $P.tp_retrans = $P.tp_Nretrans; + tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); + + if( trick_hc ) + return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data); + } +; + +/* TP4 only */ +SAME <== TP_AKWAIT TM_retrans + ( $P.tp_retrans > 0 ) + { + int error; + struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); + + IncStat(ts_retrans_cc); + $P.tp_retrans --; + $P.tp_cong_win = 1 * $P.tp_l_tpdusize; + + if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) + $P.tp_sock->so_error = error; + tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks); + } +; + +/* TP4 only */ +TP_CLOSING <== TP_AKWAIT TM_retrans + DEFAULT /* out of time */ + { + IncStat(ts_conn_gaveup); + tp_soisdisconnecting($P.tp_sock); + $P.tp_sock->so_error = ETIMEDOUT; + tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); + (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL); + $P.tp_retrans = $P.tp_Nretrans; + tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); + } +; + +/* the retrans timers had better go off BEFORE the inactivity timer does, + * if transmissions are going on. + * (i.e., TM_inact should be greater than timer for all retrans plus ack + * turnaround) + */ +/* TP4 only */ +TP_CLOSING <== TP_OPEN [ TM_inact, TM_retrans, TM_data_retrans ] + DEFAULT + { + tp_euntimeout($P, TM_data_retrans); /* all */ + tp_cuntimeout($P, TM_inact); + tp_cuntimeout($P, TM_sendack); + + IncStat(ts_conn_gaveup); + tp_soisdisconnecting($P.tp_sock); + $P.tp_sock->so_error = ETIMEDOUT; + tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); + (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL); + $P.tp_retrans = $P.tp_Nretrans; + tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); + } +; + +/* TP4 only */ +SAME <== TP_OPEN TM_retrans + ( $P.tp_retrans > 0 ) + { + $P.tp_cong_win = 1 * $P.tp_l_tpdusize; + /* resume XPD */ + if ( $P.tp_Xsnd.sb_mb ) { + struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc); + int shift; + + IFTRACE(D_XPD) + tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna", + $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt, + $P.tp_snduna); + ENDTRACE + IFDEBUG(D_XPD) + dump_mbuf(m, "XPD retrans emitting M"); + ENDDEBUG + IncStat(ts_retrans_xpd); + $P.tp_retrans --; + shift = max($P.tp_Nretrans - $P.tp_retrans, 6); + (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); + tp_ctimeout($P, TM_retrans, ((int)$P.tp_dt_ticks) << shift); + } + } +; + +/* TP4 only */ +SAME <== TP_OPEN TM_data_retrans + ($P.tp_rxtshift < TP_NRETRANS) + { + $P.tp_rxtshift++; + (void) tp_data_retrans($P); + } +; + +/* TP4 only */ +SAME <== TP_CLOSING TM_retrans + ( $P.tp_retrans > 0 ) + { + $P.tp_retrans --; + (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL); + IncStat(ts_retrans_dr); + tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); + } +; + +/* TP4 only */ +TP_REFWAIT <== TP_CLOSING TM_retrans + DEFAULT /* no more retrans - gave up */ + { + $P.tp_sock->so_error = ETIMEDOUT; + $P.tp_refstate = REF_FROZEN; + tp_recycle_tsuffix( $P ); + tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks); + } +; + +/* + * The resources are kept around until the ref timer goes off. + * The suffices are wiped out sooner so they can be reused right away. + */ +/* applicable in TP4, TP0 */ +TP_CLOSED <== TP_REFWAIT TM_reference + DEFAULT + { + tp_freeref($P.tp_lref); + tp_detach($P); + } +; + +/* applicable in TP4, TP0 */ +/* A duplicate CR from connectionless network layer can't happen */ +SAME <== TP_OPEN [ CR_TPDU, CC_TPDU ] + DEFAULT + { + if( $P.tp_class != TP_CLASS_0) { + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + if ( $E.ev_number == CC_TPDU ) + (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); + } + /* ignore it if class 0 - state tables are blank for this */ + } +; + +/* applicable in TP4, TP0 */ +SAME <== TP_OPEN T_DATA_req + DEFAULT + { + IFTRACE(D_DATA) + tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb", + $P.tp_sndnxt, $P.tp_snduna, $P.tp_fcredit, $P); + ENDTRACE + + tp_send($P); + } +; + +/* TP4 only */ +SAME <== TP_OPEN T_XPD_req + DEFAULT + /* T_XPD_req was issued by sosend iff xpd socket buf was empty + * at time of sosend(), + * AND (which means) there were no unacknowledged XPD tpdus outstanding! + */ + { + int error = 0; + + /* resume XPD */ + if ( $P.tp_Xsnd.sb_mb ) { + struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc); + /* m_copy doesn't preserve the m_xlink field, but at this pt. + * that doesn't matter + */ + + IFTRACE(D_XPD) + tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna", + $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt, + $P.tp_snduna); + ENDTRACE + IFDEBUG(D_XPD) + printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc); + dump_mbuf(m, "XPD req emitting M"); + ENDDEBUG + error = + tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); + $P.tp_retrans = $P.tp_Nretrans; + + tp_ctimeout($P, TM_retrans, (int)$P.tp_rxtcur); + SEQ_INC($P, $P.tp_Xsndnxt); + } + if(trick_hc) + return error; + } +; + +/* TP4, faked ack in TP0 when cons send completes */ +SAME <== TP_OPEN AK_TPDU + ( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq) ) + + /* tp_goodack == true means + * EITHER it actually acked something heretofore unacknowledged + * OR no news but the credit should be processed. + */ + { + struct sockbuf *sb = &$P.tp_sock->so_snd; + + IFDEBUG(D_ACKRECV) + printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt); + ENDDEBUG + if( $P.tp_class != TP_CLASS_0) { + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + } + sbwakeup(sb); + IFDEBUG(D_ACKRECV) + printf("GOOD ACK new sndnxt 0x%x\n", $P.tp_sndnxt); + ENDDEBUG + } +; + +/* TP4, and TP0 after sending a CC or possibly a CR */ +SAME <== TP_OPEN AK_TPDU + DEFAULT + { + IFTRACE(D_ACKRECV) + tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq", + $$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0); + ENDTRACE + if( $P.tp_class != TP_CLASS_0 ) { + + if ( !$$.e_fcc_present ) { + /* send ACK with FCC */ + IncStat( ts_ackreason[_ACK_FCC_] ); + (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL); + } + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + } + } +; + +/* NBS(47) */ + /* goes in at *** */ + /* just so happens that this is never true now, because we allow + * only 1 packet in the queue at once (this could be changed) + if ( $P.tp_Xsnd.sb_mb ) { + struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??); + + (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); + $P.tp_retrans = $P.tp_Nretrans; + tp_ctimeout($P, TM_retrans, (int)$P.tp_xpd_ticks); + SEQ_INC($P, $P.tp_Xsndnxt); + } + */ + /* end of the above hack */ + +/* TP4 only */ +SAME <== TP_OPEN XAK_TPDU + ( tp_goodXack($P, $$.e_seq) ) + /* tp_goodXack checks for good ack, removes the correct + * tpdu from the queue and returns 1 if ack was legit, 0 if not. + * also updates tp_Xuna + */ + { + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + tp_cuntimeout($P, TM_retrans); + + sbwakeup( &$P.tp_sock->so_snd ); + + /* resume normal data */ + tp_send($P); + } +; + +/* TP4, and TP0 after sending a CC or possibly a CR */ +SAME <== TP_OPEN XAK_TPDU + DEFAULT + { + IFTRACE(D_ACKRECV) + tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0); + ENDTRACE + if( $P.tp_class != TP_CLASS_0 ) { + tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); + } + } +; + +/* TP4 only */ +SAME <== TP_OPEN TM_sendack + DEFAULT + { + int timo; + IFTRACE(D_TIMER) + tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe, + $P.tp_sent_lcdt, 0); + ENDTRACE + IncPStat($P, tps_n_TMsendack); + (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); + if ($P.tp_fcredit == 0) { + if ($P.tp_rxtshift < TP_MAXRXTSHIFT) + $P.tp_rxtshift++; + timo = ($P.tp_dt_ticks) << $P.tp_rxtshift; + } else + timo = $P.tp_sendack_ticks; + tp_ctimeout($P, TM_sendack, timo); + } +; + +/* TP0 only */ +SAME <== TP_OPEN T_USR_rcvd + ($P.tp_class == TP_CLASS_0) + { + if (sbspace(&$P.tp_sock->so_rcv) > 0) + tp0_openflow($P); + } +; + +/* TP4 only */ + /* If old credit was zero, + * we'd better inform other side that we now have space + * But this is not enough. Sender might not yet have + * seen an ack with cdt 0 but it might still think the + * window is closed, so it's going to wait. + * Best to send an ack each time. + * Strictly speaking, this ought to be a function of the + * general ack strategy. + */ +SAME <== TP_OPEN T_USR_rcvd + DEFAULT + { + if( trick_hc ) { + SeqNum ack_thresh; + /* + * If the upper window edge has advanced a reasonable + * amount beyond what was known, send an ACK. + * A reasonable amount is 2 packets, unless the max window + * is only 1 or 2 packets, in which case we + * should send an ack for any advance in the upper window edge. + */ + LOCAL_CREDIT($P); + ack_thresh = SEQ_SUB($P, $P.tp_lcredit + $P.tp_rcvnxt, + ($P.tp_maxlcredit > 2 ? 2 : 1)); + if (SEQ_GT($P, ack_thresh, $P.tp_sent_uwe)) { + IncStat(ts_ackreason[_ACK_USRRCV_]); + $P.tp_flags &= ~TPF_DELACK; + return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); + } + } + } +; + +/* applicable in TP4, TP0 */ +SAME <== TP_REFWAIT [ T_USR_rcvd, T_USR_Xrcvd ] + DEFAULT + /* This happens if other end sent a DR when the user was waiting + * on a receive. + * Processing the DR includes putting us in REFWAIT state. + */ + { + if(trick_hc) + return ECONNABORTED; + } +; + +/* TP0 only */ +TP_REFWAIT <== [ TP_OPEN, TP_CRSENT, TP_LISTENING ] T_NETRESET + ( $P.tp_class != TP_CLASS_4 ) + /* 0 or (4 and 0) */ + /* in OPEN class will be 0 or 4 but not both */ + /* in CRSENT or LISTENING it could be in negotiation, hence both */ + /* Actually, this shouldn't ever happen in LISTENING */ + { + ASSERT( $P.tp_state != TP_LISTENING ); + tp_indicate(T_DISCONNECT, $P, ECONNRESET); + tp_soisdisconnected($P); + } +; + +/* TP4: ignore resets */ +SAME <== [ TP_OPEN, TP_CRSENT, TP_AKWAIT, + TP_CLOSING, TP_LISTENING ] T_NETRESET + DEFAULT + NULLACTION +; + +/* applicable in TP4, TP0 */ +SAME <== [ TP_CLOSED, TP_REFWAIT ] T_NETRESET + DEFAULT + NULLACTION +; + +/* C'EST TOUT */ |