date and time created 88/12/14 15:30:08 by sklower
authorKeith Sklower <sklower@ucbvax.Berkeley.EDU>
Thu, 15 Dec 1988 07:30:08 +0000 (23:30 -0800)
committerKeith Sklower <sklower@ucbvax.Berkeley.EDU>
Thu, 15 Dec 1988 07:30:08 +0000 (23:30 -0800)
SCCS-vsn: sys/netiso/tp_pcb.c 7.1

usr/src/sys/netiso/tp_pcb.c [new file with mode: 0644]

diff --git a/usr/src/sys/netiso/tp_pcb.c b/usr/src/sys/netiso/tp_pcb.c
new file mode 100644 (file)
index 0000000..6d2a82a
--- /dev/null
@@ -0,0 +1,913 @@
+/***********************************************************
+               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
+ */
+/* 
+ * ARGO TP
+ *
+ * $Header: tp_pcb.c,v 5.4 88/11/18 17:28:24 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_pcb.c,v $
+ *
+ *
+ * This is the initialization and cleanup stuff - 
+ * for the tp machine in general as well as  for the individual pcbs.
+ * tp_init() is called at system startup.  tp_attach() and tp_getref() are
+ * called when a socket is created.  tp_detach() and tp_freeref()
+ * are called during the closing stage and/or when the reference timer 
+ * goes off. 
+ * tp_soisdisconnecting() and tp_soisdisconnected() are tp-specific 
+ * versions of soisconnect*
+ * and are called (obviously) during the closing phase.
+ *
+ */
+
+#ifndef lint
+static char *rcsid = "$Header: tp_pcb.c,v 5.4 88/11/18 17:28:24 nhall Exp $";
+#endif lint
+
+#include "argoxtwentyfive.h"
+#include "types.h"
+#include "param.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "protosw.h"
+#include "errno.h"
+#include "time.h"
+#include "../netiso/tp_param.h"
+#include "../netiso/tp_timer.h"
+#include "../netiso/tp_ip.h"
+#include "../netiso/tp_stat.h"
+#include "../netiso/tp_pcb.h"
+#include "../netiso/argo_debug.h"
+#include "../netiso/tp_tpdu.h"
+#include "../netiso/tp_trace.h"
+#include "../netiso/tp_meas.h"
+#include "../netiso/tp_seq.h"
+#include "../netiso/tp_clnp.h"
+
+/* list of reference structures */
+struct tp_ref tp_ref[N_TPREF];
+
+struct tp_param tp_param = {
+       1,                              /*  configured          */
+};
+
+/* ticks are in units of: 
+ * 500 nano-fortnights ;-) or
+ * 500 ms or 
+ * 1/2 second 
+ */
+
+struct tp_conn_param tp_conn_param[] = {
+       /* ISO_CLNS: TP4 CONNECTION LESS */
+       {
+               TP_NRETRANS,    /* short p_Nretrans;  */
+               20,             /* 10 sec */    /* short p_dr_ticks;  */
+
+               20,             /* 10 sec */    /* short p_cc_ticks; */
+               20,             /* 10 sec */    /* short p_dt_ticks; */
+
+               40,             /* 20 sec */    /* short p_x_ticks;      */
+               80,             /* 40 sec */    /* short p_cr_ticks;*/
+
+               240,    /* 2 min */     /* short p_keepalive_ticks;*/
+               10,             /* 5 sec */     /* short p_sendack_ticks;  */
+
+               600,    /* 5 min */     /* short p_ref_ticks;   */
+               360,    /* 3 min */     /* short p_inact_ticks; */
+
+               (short) 100,                    /* short p_lcdtfract */
+               (short) TP_SOCKBUFSIZE, /* short p_winsize */
+               TP_TPDUSIZE,                    /* u_char p_tpdusize */
+
+               TPACK_WINDOW,                   /* 4 bits p_ack_strat */
+               TPRX_USE_CW | TPRX_FASTSTART, 
+                                                               /* 4 bits p_rx_strat*/
+               TP_CLASS_4 | TP_CLASS_0,/* 5 bits p_class */
+               1,                                              /* 1 bit xtd format */
+               1,                                              /* 1 bit xpd service */
+               1,                                              /* 1 bit use_checksum */
+               0,                                              /* 1 bit use net xpd */
+               0,                                              /* 1 bit use rcc */
+               0,                                              /* 1 bit use efc */
+               0,                                              /* no disc indications */
+               0,                                              /* don't change params */
+               ISO_CLNS,                               /* p_netservice */
+       },
+       /* IN_CLNS: TP4 CONNECTION LESS */
+       {
+               TP_NRETRANS,    /* short p_Nretrans;  */
+               20,             /* 10 sec */    /* short p_dr_ticks;  */
+
+               20,             /* 10 sec */    /* short p_cc_ticks; */
+               20,             /* 10 sec */    /* short p_dt_ticks; */
+
+               40,             /* 20 sec */    /* short p_x_ticks;      */
+               80,             /* 40 sec */    /* short p_cr_ticks;*/
+
+               240,    /* 2 min */     /* short p_keepalive_ticks;*/
+               10,             /* 5 sec */     /* short p_sendack_ticks;  */
+
+               600,    /* 5 min */     /* short p_ref_ticks;   */
+               360,    /* 3 min */     /* short p_inact_ticks; */
+
+               (short) 100,                    /* short p_lcdtfract */
+               (short) TP_SOCKBUFSIZE, /* short p_winsize */
+               TP_TPDUSIZE,                    /* u_char p_tpdusize */
+
+               TPACK_WINDOW,                   /* 4 bits p_ack_strat */
+               TPRX_USE_CW | TPRX_FASTSTART, 
+                                                               /* 4 bits p_rx_strat*/
+               TP_CLASS_4,                             /* 5 bits p_class */
+               1,                                              /* 1 bit xtd format */
+               1,                                              /* 1 bit xpd service */
+               1,                                              /* 1 bit use_checksum */
+               0,                                              /* 1 bit use net xpd */
+               0,                                              /* 1 bit use rcc */
+               0,                                              /* 1 bit use efc */
+               0,                                              /* no disc indications */
+               0,                                              /* don't change params */
+               IN_CLNS,                                /* p_netservice */
+       },
+       /* ISO_CONS: TP0 CONNECTION MODE */
+       {
+               TP_NRETRANS,                    /* short p_Nretrans;  */
+               0,              /* n/a */               /* short p_dr_ticks; */
+
+               40,             /* 20 sec */    /* short p_cc_ticks; */
+               0,              /* n/a */               /* short p_dt_ticks; */
+
+               0,              /* n/a */               /* short p_x_ticks;     */
+               360,    /* 3  min */    /* short p_cr_ticks;*/
+
+               0,              /* n/a */               /* short p_keepalive_ticks;*/
+               0,              /* n/a */               /* short p_sendack_ticks; */
+
+               600,    /* for cr/cc to clear *//* short p_ref_ticks;   */
+               0,              /* n/a */               /* short p_inact_ticks; */
+
+               /* Use tp4 defaults just in case the user changes ONLY
+                * the class 
+                */
+               (short) 100,                    /* short p_lcdtfract */
+               (short) TP0_SOCKBUFSIZE,        /* short p_winsize */
+               TP0_TPDUSIZE,                   /* 8 bits p_tpdusize */
+
+               0,                                              /* 4 bits p_ack_strat */
+               0,                                              /* 4 bits p_rx_strat*/
+               TP_CLASS_0,                             /* 5 bits p_class */
+               0,                                              /* 1 bit xtd format */
+               0,                                              /* 1 bit xpd service */
+               0,                                              /* 1 bit use_checksum */
+               0,                                              /* 1 bit use net xpd */
+               0,                                              /* 1 bit use rcc */
+               0,                                              /* 1 bit use efc */
+               0,                                              /* no disc indications */
+               0,                                              /* don't change params */
+               ISO_CONS,                               /* p_netservice */
+       },
+       /* ISO_COSNS: TP4 CONNECTION LESS SERVICE over CONSNS */
+       {
+               TP_NRETRANS,    /* short p_Nretrans;  */
+               40,             /* 20 sec */    /* short p_dr_ticks;  */
+
+               40,             /* 20 sec */    /* short p_cc_ticks; */
+               80,             /* 40 sec */    /* short p_dt_ticks; */
+
+               120,            /* 1 min */     /* short p_x_ticks;      */
+               360,            /* 3 min */     /* short p_cr_ticks;*/
+
+               360,    /* 3 min */     /* short p_keepalive_ticks;*/
+               20,             /* 10 sec */    /* short p_sendack_ticks;  */
+
+               600,    /* 5 min */     /* short p_ref_ticks;   */
+               480,    /* 4 min */     /* short p_inact_ticks; */
+
+               (short) 100,                    /* short p_lcdtfract */
+               (short) TP0_SOCKBUFSIZE,        /* short p_winsize */
+               TP0_TPDUSIZE,                   /* u_char p_tpdusize */
+
+               TPACK_WINDOW,                   /* 4 bits p_ack_strat */
+               TPRX_USE_CW ,                   /* No fast start */ 
+                                                               /* 4 bits p_rx_strat*/
+               TP_CLASS_4 | TP_CLASS_0,/* 5 bits p_class */
+               0,                                              /* 1 bit xtd format */
+               1,                                              /* 1 bit xpd service */
+               1,                                              /* 1 bit use_checksum */
+               0,                                              /* 1 bit use net xpd */
+               0,                                              /* 1 bit use rcc */
+               0,                                              /* 1 bit use efc */
+               0,                                              /* no disc indications */
+               0,                                              /* don't change params */
+               ISO_COSNS,                              /* p_netservice */
+       },
+};
+
+#ifdef INET
+int            in_putnetaddr();
+int            in_getnetaddr();
+int    in_putsufx(); 
+int    in_getsufx(); 
+int    in_recycle_tsuffix(); 
+int    tpip_mtu(); 
+int    in_pcbbind(); 
+int    in_pcbconnect(); 
+int    in_pcbdisconnect(); 
+int    in_pcbdetach(); 
+int    in_pcballoc(); 
+int    tpip_output(); 
+int    tpip_output_dg(); 
+struct inpcb   tp_inpcb;
+#endif INET
+#ifdef ISO
+int            iso_putnetaddr();
+int            iso_getnetaddr();
+int    iso_putsufx(); 
+int    iso_getsufx(); 
+int    iso_recycle_tsuffix(); 
+int            tpclnp_mtu(); 
+int            iso_pcbbind(); 
+int            iso_pcbconnect(); 
+int            iso_pcbdisconnect(); 
+int    iso_pcbdetach(); 
+int    iso_pcballoc(); 
+int    tpclnp_output(); 
+int    tpclnp_output_dg(); 
+int            iso_nlctloutput();
+struct isopcb  tp_isopcb;
+#endif ISO
+#if NARGOXTWENTYFIVE > 0
+int            iso_putnetaddr();
+int            iso_getnetaddr();
+int    iso_putsufx(); 
+int    iso_getsufx(); 
+int    iso_recycle_tsuffix(); 
+int            tpcons_mtu(); 
+int            iso_pcbbind(); 
+int            iso_pcbconnect(); 
+int            iso_pcbdisconnect(); 
+int    iso_pcbdetach(); 
+int    iso_pcballoc(); 
+int    tpcons_output(); 
+int    tpcons_output_dg(); 
+struct isopcb  tp_isopcb;
+#endif NARGOXTWENTYFIVE
+
+struct nl_protosw nl_protosw[] = {
+       /* ISO_CLNS */
+#ifdef ISO
+       { AF_ISO, iso_putnetaddr, iso_getnetaddr,
+               iso_putsufx, iso_getsufx,
+               iso_recycle_tsuffix,
+               tpclnp_mtu, iso_pcbbind, iso_pcbconnect,
+               iso_pcbdisconnect,      iso_pcbdetach,
+               iso_pcballoc,
+               tpclnp_output, tpclnp_output_dg, iso_nlctloutput,
+               (caddr_t) &tp_isopcb,
+               },
+#endif ISO
+       /* IN_CLNS */
+#ifdef INET
+       { AF_INET, in_putnetaddr, in_getnetaddr,
+               in_putsufx, in_getsufx,
+               in_recycle_tsuffix,
+               tpip_mtu, in_pcbbind, in_pcbconnect,
+               in_pcbdisconnect,       in_pcbdetach,
+               in_pcballoc,
+               tpip_output, tpip_output_dg, /* nl_ctloutput */ NULL,
+               (caddr_t) &tp_inpcb,
+               },
+#endif INET
+       /* ISO_CONS */
+#ifdef ISO
+#if    NARGOXTWENTYFIVE > 0
+       { AF_ISO, iso_putnetaddr, iso_getnetaddr,
+               iso_putsufx, iso_getsufx,
+               iso_recycle_tsuffix,
+               tpcons_mtu, iso_pcbbind, iso_pcbconnect,
+               iso_pcbdisconnect,      iso_pcbdetach,
+               iso_pcballoc,
+               tpcons_output, tpcons_output_dg, iso_nlctloutput,
+               (caddr_t) &tp_isopcb,
+               },
+#endif NARGOXTWENTYFIVE
+#endif ISO
+       { 0, 0, 0,
+               0, 0,
+               0,
+               0, 0, 0,
+               0,      0,
+               0,
+               0, 0, 0,
+               (caddr_t) 0,
+               }
+};
+
+/*
+ * NAME:  tp_init()
+ *
+ * CALLED FROM:
+ *  autoconf through the protosw structure
+ *
+ * FUNCTION:
+ *  initialize tp machine
+ *
+ * RETURNS:  Nada
+ *
+ * SIDE EFFECTS:
+ * 
+ * NOTES:
+ */
+void
+tp_init()
+{
+       static int      init_done=0;
+       void            tp_timerinit();
+
+       if (init_done++)
+               return;
+
+#ifndef lint
+       if ( (sizeof(struct tp_pcb) >= MLEN)  || (sizeof(struct tp_pcb_aux) >= MLEN)  ){
+               tp_param.tpp_configed = 0;
+               printf(
+                       "TP not configured !!! pcb (0x%x, %d) or aux (0x%x, %d) too big!\n",
+                       sizeof(struct tp_pcb), sizeof(struct tp_pcb), 
+                       sizeof(struct tp_pcb_aux), sizeof(struct tp_pcb_aux));
+               printf("MLEN (0x%x, %d)\n", MLEN, MLEN);
+       }
+#endif lint
+
+       /* FOR INET */
+       tp_inpcb.inp_next = tp_inpcb.inp_prev = &tp_inpcb;
+       /* FOR ISO */
+       tp_isopcb.isop_next = tp_isopcb.isop_prev = &tp_isopcb;
+
+       tp_timerinit();
+       bzero((caddr_t)&tp_stat, sizeof(struct tp_stat));
+}
+
+/*
+ * NAME:       tp_soisdisconnecting()
+ *
+ * CALLED FROM:
+ *  tp.trans
+ *
+ * FUNCTION and ARGUMENTS:
+ *  Set state of the socket (so) to reflect that fact that we're disconnectING
+ *
+ * RETURNS:    Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ *  This differs from the regular soisdisconnecting() in that the latter
+ *  also sets the SS_CANTRECVMORE and SS_CANTSENDMORE flags.
+ *  We don't want to set those flags because those flags will cause
+ *  a SIGPIPE to be delivered in sosend() and we don't like that.
+ *  If anyone else is sleeping on this socket, wake 'em up.
+ */
+void
+tp_soisdisconnecting(so)
+       register struct socket *so;
+{
+       so->so_state &= ~SS_ISCONNECTING;
+       so->so_state |= SS_ISDISCONNECTING;
+       if (so->so_head) {
+               if (!soqremque(so, 0) && !soqremque(so, 1))
+                       panic("tp_soisdisconnecting");
+               so->so_head = 0;
+       }
+       wakeup((caddr_t)&so->so_timeo);
+       sowwakeup(so);
+       sorwakeup(so);
+       IFPERF(sototpcb(so))
+               register struct tp_pcb *tpcb = sototpcb(so);
+               u_int   fsufx, lsufx;
+
+               bcopy ( tpcb->tp_fsuffix, &fsufx, sizeof(u_int) );
+               bcopy ( tpcb->tp_lsuffix, &lsufx, sizeof(u_int) );
+
+               tpmeas( tpcb->tp_lref, TPtime_close, 
+                       &time, fsufx, lsufx, tpcb->tp_fref);
+               tpcb->tp_perf_on = 0; /* turn perf off */
+       ENDPERF
+}
+
+
+/*
+ * NAME: tp_soisdisconnected()
+ *
+ * CALLED FROM:
+ *     tp.trans        
+ *
+ * FUNCTION and ARGUMENTS:
+ *  Set state of the socket (so) to reflect that fact that we're disconnectED
+ *  Set the state of the reference structure to closed, and
+ *  recycle the suffix.
+ *  Start a reference timer.
+ *
+ * RETURNS:    Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ *  This differs from the regular soisdisconnected() in that the latter
+ *  also sets the SS_CANTRECVMORE and SS_CANTSENDMORE flags.
+ *  We don't want to set those flags because those flags will cause
+ *  a SIGPIPE to be delivered in sosend() and we don't like that.
+ *  If anyone else is sleeping on this socket, wake 'em up.
+ */
+void
+tp_soisdisconnected(tpcb)
+       register struct tp_pcb  *tpcb;
+{
+       register struct socket  *so = tpcb->tp_sock;
+
+       so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING);
+       if (so->so_head) {
+               if (!soqremque(so, 0) && !soqremque(so, 1))
+                       panic("tp_soisdisconnected");
+               so->so_head = 0;
+       }
+       wakeup((caddr_t)&so->so_timeo);
+       sowwakeup(so);
+       sorwakeup(so);
+       IFPERF(sototpcb(so))
+               register struct tp_pcb *tpcb = sototpcb(so);
+               u_int   fsufx, lsufx;
+
+               /* CHOKE */
+               bcopy ( tpcb->tp_fsuffix, &fsufx, sizeof(u_int) );
+               bcopy ( tpcb->tp_lsuffix, &lsufx, sizeof(u_int) );
+
+               tpmeas( tpcb->tp_lref, TPtime_close, 
+                       &time, &lsufx, &fsufx, tpcb->tp_fref);
+               tpcb->tp_perf_on = 0; /* turn perf off */
+       ENDPERF
+
+       tpcb->tp_refp->tpr_state = REF_FROZEN;
+       tp_recycle_tsuffix( tpcb );
+       tp_etimeout(tpcb->tp_refp, TM_reference, 0,0,0, (int)tpcb->tp_refer_ticks);
+}
+
+int tp_maxrefopen;  /* highest reference # of the set of open tp connections */
+
+/*
+ * NAME:       tp_freeref()
+ *
+ * CALLED FROM:
+ *  tp.trans when the reference timer goes off, and
+ *  from tp_attach() and tp_detach() when a tpcb is partially set up but not
+ *  set up enough to have a ref timer set for it, and it's discarded
+ *  due to some sort of error or an early close()
+ *
+ * FUNCTION and ARGUMENTS:
+ *  Frees the reference represented by (r) for re-use.
+ *
+ * RETURNS: Nothing
+ * 
+ * SIDE EFFECTS:
+ *
+ * NOTES:      better be called at clock priority !!!!!
+ */
+void
+tp_freeref(r)
+       register struct tp_ref *r;
+{
+       IFDEBUG(D_TIMER)
+               printf("tp_freeref called for ref %d maxrefopen %d\n", 
+               r - tp_ref, tp_maxrefopen);
+       ENDDEBUG
+       IFTRACE(D_TIMER)
+               tptrace(TPPTmisc, "tp_freeref ref tp_maxrefopen",
+               r - tp_ref, tp_maxrefopen, 0, 0);
+       ENDTRACE
+       r->tpr_state = REF_FREE;
+       IFDEBUG(D_CONN)
+               printf("tp_freeref: CLEARING tpr_pcb 0x%x\n", r->tpr_pcb);
+       ENDDEBUG
+       r->tpr_pcb = (struct tp_pcb *)0;
+
+       r = &tp_ref[tp_maxrefopen];
+
+       while( tp_maxrefopen > 0 ) {
+               if(r->tpr_state )
+                       break;
+               tp_maxrefopen--;
+               r--;
+       }
+       IFDEBUG(D_TIMER)
+               printf("tp_freeref ends w/ maxrefopen %d\n", tp_maxrefopen);
+       ENDDEBUG
+}
+
+/*
+ * NAME:  tp_getref()
+ *
+ * CALLED FROM:
+ *  tp_attach()
+ *
+ * FUNCTION and ARGUMENTS:
+ *  obtains the next free reference and allocates the appropriate
+ *  ref structure, links that structure to (tpcb) 
+ *
+ * RETURN VALUE:
+ *     a reference number
+ *  or TP_ENOREF
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+static RefNum
+tp_getref(tpcb) 
+       register struct tp_pcb *tpcb;
+{
+       register struct tp_ref  *r = tp_ref; 
+       register int                    i=1;
+
+       r++; /* tp_ref[0] is never used */
+
+       /* REF_FREE is zero */
+       while( r->tpr_state ) {
+               r++; 
+               if ( i == N_TPREF ) {
+                       return TP_ENOREF;
+               }
+               i++;
+       }
+       r->tpr_state = REF_OPENING;
+       if (tp_maxrefopen < i) 
+               tp_maxrefopen = i;
+       r->tpr_pcb = tpcb;
+       tpcb->tp_refp = r;
+
+       return i;
+}
+
+/*
+ * NAME: tp_attach()
+ *
+ * CALLED FROM:
+ *     tp_usrreq, PRU_ATTACH
+ *
+ * FUNCTION and ARGUMENTS:
+ *  given a socket (so) and a protocol family (dom), allocate a tpcb
+ *  and ref structure, initialize everything in the structures that
+ *  needs to be initialized.
+ *
+ * RETURN VALUE:
+ *  0 ok
+ *  EINVAL if DEBUG(X) in is on and a disaster has occurred
+ *  ENOPROTOOPT if TP hasn't been configured or if the
+ *   socket wasn't created with tp as its protocol
+ *  EISCONN if this socket is already part of a connection
+ *  ETOOMANYREFS if ran out of tp reference numbers.
+ *  E* whatever error is returned from soreserve()
+ *    for from the network-layer pcb allocation routine
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ */
+int
+tp_attach(so,dom)
+       struct socket   *so;
+       int                     dom;
+{
+       register struct tp_pcb  *tpcb;
+       register struct mbuf    *m;
+       register struct mbuf    *p;
+       int                                     error;
+       int                                     protocol = so->so_proto->pr_protocol;
+       extern struct tp_conn_param tp_conn_param[];
+
+       IFDEBUG(D_CONN)
+               printf("tp_attach:dom 0x%x so 0x%x ", dom, so);
+       ENDDEBUG
+       IFTRACE(D_CONN)
+               tptrace(TPPTmisc, "tp_attach:dom so", dom, so, 0, 0);
+       ENDTRACE
+       if ( ! tp_param.tpp_configed ) {
+               error = ENOPROTOOPT; /* protocol not available */
+               goto bad2;
+       }
+
+       if (so->so_pcb != NULL) { 
+               return EISCONN; /* socket already part of a connection*/
+       }
+
+       error = soreserve(so, TP_SOCKBUFSIZE, TP_SOCKBUFSIZE);
+               /* later an ioctl will allow reallocation IF still in closed state */
+
+       if (error)
+               goto bad2;
+
+       MGET(m, M_DONTWAIT, TPMT_PCB);  /* for tpcb, main half */
+       if (m == NULL) {
+               error = ENOBUFS;
+               goto bad2;
+       }
+
+       tpcb = mtod( m, struct tp_pcb * );
+       bzero( (caddr_t)tpcb, sizeof (struct tp_pcb) );
+
+       MGET(p, M_DONTWAIT, TPMT_PCB);  /* for the tpcb, auxilliary half */
+       if (p == NULL) {
+               error = ENOBUFS;
+               m_free(m); /* which is tpcb */
+               goto bad2;
+       } else {
+               p->m_len = sizeof(struct tp_pcb_aux);
+               p->m_act = MNULL;
+               tpcb->tp_aux = mtod(p, struct tp_pcb_aux *);
+               bzero( (caddr_t)tpcb->tp_aux, sizeof (struct tp_pcb_aux) );
+       }
+
+       if ( ((tpcb->tp_lref = tp_getref(tpcb)) &  TP_ENOREF) != 0 ) { 
+               error = ETOOMANYREFS; 
+               goto bad3;
+       }
+       tpcb->tp_sock =  so;
+       tpcb->tp_domain = dom;
+       if (protocol<ISOPROTO_TP4) {
+               tpcb->tp_netservice = ISO_CONS;
+               tpcb->tp_snduna = (SeqNum) -1;/* kludge so the pseudo-ack from the CR/CC
+                                                                * will generate correct fake-ack values
+                                                                */
+       } else {
+               tpcb->tp_netservice = (dom== AF_INET)?IN_CLNS:ISO_CLNS;
+               /* the default */
+       }
+       tpcb->_tp_param = tp_conn_param[tpcb->tp_netservice];
+
+       tpcb->tp_cong_win = 1;  
+       tpcb->tp_state = TP_CLOSED;
+       tpcb->tp_vers  = TP_VERSION;
+
+                  /* Spec says default is 128 octets,
+                       * that is, if the tpdusize argument never appears, use 128.
+                       * As the initiator, we will always "propose" the 2048
+                       * size, that is, we will put this argument in the CR 
+                       * always, but accept what the other side sends on the CC.
+                       * If the initiator sends us something larger on a CR,
+                       * we'll respond w/ this.
+                       * Our maximum is 4096.  See tp_chksum.c comments.
+                       */
+       tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize;
+
+       tpcb->tp_seqmask  = TP_NML_FMT_MASK;
+       tpcb->tp_seqbit  =  TP_NML_FMT_BIT;
+       tpcb->tp_seqhalf  =  tpcb->tp_seqbit >> 1;
+       tpcb->tp_sndhiwat = (SeqNum) - 1; /* a kludge but it works */
+       tpcb->tp_s_subseq = 0;
+
+       /* attach to a network-layer protoswitch */
+       /* new way */
+       tpcb->tp_nlproto = & nl_protosw[tpcb->tp_netservice];
+       ASSERT( tpcb->tp_nlproto->nlp_afamily == tpcb->tp_domain);
+#ifdef notdef
+       /* OLD WAY */
+       /* TODO: properly, this search would be on the basis of 
+       * domain,netservice or just netservice only (if you have
+       * IN_CLNS, ISO_CLNS, and ISO_CONS)
+       */
+       tpcb->tp_nlproto = nl_protosw;
+       while(tpcb->tp_nlproto->nlp_afamily != tpcb->tp_domain )  {
+               if( tpcb->tp_nlproto->nlp_afamily == 0 ) {
+                       error = EAFNOSUPPORT;
+                       goto bad4;
+               }
+               tpcb->tp_nlproto ++;
+       }
+#endif notdef
+
+       /* xx_pcballoc sets so_pcb */
+       if ( error =  (tpcb->tp_nlproto->nlp_pcballoc) ( 
+                                                       so, tpcb->tp_nlproto->nlp_pcblist ) ) {
+               goto bad4;
+       }
+
+       if( dom == AF_INET )
+               sotoinpcb(so)->inp_ppcb = (caddr_t) tpcb;
+               /* nothing to do for iso case */
+
+       tpcb->tp_npcb = (caddr_t) so->so_pcb;
+       so->so_tpcb = (caddr_t) tpcb;
+
+       return 0;
+
+bad4:
+       IFDEBUG(D_CONN)
+               printf("BAD4 in tp_attach, so 0x%x\n", so);
+       ENDDEBUG
+       tp_freeref(tpcb->tp_refp);
+
+bad3:
+       IFDEBUG(D_CONN)
+               printf("BAD3 in tp_attach, so 0x%x\n", so);
+       ENDDEBUG
+
+       m_free(dtom(tpcb)); /* never a cluster  */
+       m_free(dtom(tpcb->tp_aux)); /* never a cluster */
+
+bad2:
+       IFDEBUG(D_CONN)
+               printf("BAD2 in tp_attach, so 0x%x\n", so);
+       ENDDEBUG
+       so->so_pcb = 0;
+       so->so_tpcb = 0;
+       sofree(so);
+
+bad:
+       IFDEBUG(D_CONN)
+               printf("BAD in tp_attach, so 0x%x\n", so);
+       ENDDEBUG
+       return error;
+}
+
+/*
+ * NAME:  tp_detach()
+ *
+ * CALLED FROM:
+ *     tp.trans, on behalf of a user close request
+ *  and when the reference timer goes off
+ * (if the disconnect  was initiated by the protocol entity 
+ * rather than by the user)
+ *
+ * FUNCTION and ARGUMENTS:
+ *  remove the tpcb structure from the list of active or
+ *  partially active connections, recycle all the mbufs
+ *  associated with the pcb, ref structure, sockbufs, etc.
+ *  Only free the ref structure if you know that a ref timer
+ *  wasn't set for this tpcb.
+ *
+ * RETURNS:  Nada
+ *
+ * SIDE EFFECTS:
+ *
+ * NOTES:
+ *  tp_soisdisconnected() was already when this is called
+ */
+void
+tp_detach(tpcb)
+       register struct tp_pcb  *tpcb;
+{
+       void                                    tp_freeref();
+       register struct socket   *so = tpcb->tp_sock;
+
+       IFDEBUG(D_CONN)
+               printf("tp_detach(tpcb 0x%x, so 0x%x) freelist 0%x\n",
+                       tpcb,so, mfree);
+       ENDDEBUG
+       IFTRACE(D_CONN)
+               tptraceTPCB(TPPTmisc, "tp_detach tpcb so lsufx", 
+                       tpcb, so, *(int *)(tpcb->tp_lsuffix), 0);
+       ENDTRACE
+
+       if (so->so_head) {
+               if (!soqremque(so, 0) && !soqremque(so, 1))
+                       panic("sofree dq");
+               so->so_head = 0;
+       }
+
+       IFDEBUG(D_CONN)
+               printf("tp_detach(freeing RTC list snduna 0x%x rcvnxt 0x%x)\n",
+               tpcb->tp_snduna_rtc,
+               tpcb->tp_rcvnxt_rtc);
+       ENDDEBUG
+
+#define FREE_RTC_LIST(XXX)\
+       { register struct tp_rtc *xxr = XXX, *xxs; while (xxr) {\
+               xxs = xxr->tprt_next;\
+               m_freem( xxr->tprt_data );\
+               m_free( dtom(xxr) ); xxr = xxs; }\
+               XXX = (struct tp_rtc *)0;\
+       }
+
+       FREE_RTC_LIST( tpcb->tp_snduna_rtc );
+       tpcb->tp_sndhiwat_rtc = (struct tp_rtc *)0;
+
+       FREE_RTC_LIST( tpcb->tp_rcvnxt_rtc );
+
+#undef FREE_RTC_LIST
+
+       IFDEBUG(D_CONN)
+               printf("calling (...nlproto->...)(0x%x, so 0x%x)\n", 
+                       so->so_pcb, so);
+               printf("so 0x%x so_head 0x%x,  qlen %d q0len %d qlimit %d\n", 
+               so,  so->so_head,
+               so->so_q0len, so->so_qlen, so->so_qlimit);
+       ENDDEBUG
+
+       if ( tpcb->tp_flags & (TPF_CONN_DATA_OUT | TPF_DISC_DATA_OUT ) ) {
+               ASSERT( so->so_snd.sb_cc != 0 );
+               IFDEBUG(D_CONN)
+                       printf(
+                       "detach, flags 0x%x doing sbdrop on so_snd, mb 0x%x cc 0x%x\n", 
+                               tpcb->tp_flags, so->so_snd.sb_mb, so->so_snd.sb_cc);
+                       dump_mbuf( so->so_snd.sb_mb, "detach so snd: \n");
+               ENDDEBUG
+               if ( so->so_snd.sb_cc != 0 )
+                       sbdrop( &so->so_snd, so->so_snd.sb_cc);
+               tpcb->tp_flags &= ~(TPF_CONN_DATA_OUT | TPF_DISC_DATA_OUT); 
+       }
+       if ( tpcb->tp_flags & (TPF_DISC_DATA_IN | TPF_CONN_DATA_IN ) ) {
+               ASSERT( tpcb->tp_Xrcv.sb_cc != 0 );
+               IFDEBUG(D_CONN)
+                       printf(
+                       "detach, flags 0x%x doing sbdrop on tp_Xrcv, mb 0x%x cc 0x%x\n", 
+                               tpcb->tp_flags, tpcb->tp_Xrcv.sb_mb, tpcb->tp_Xrcv.sb_cc);
+                       dump_mbuf( tpcb->tp_Xrcv.sb_mb, "detach Xrcv: \n");
+               ENDDEBUG
+               if( tpcb->tp_Xrcv.sb_cc != 0 )
+                       sbdrop( &tpcb->tp_Xrcv, tpcb->tp_Xrcv.sb_cc);
+               tpcb->tp_flags &= ~(TPF_CONN_DATA_IN | TPF_DISC_DATA_IN); 
+       }
+
+       IFDEBUG(D_CONN)
+               printf("so_snd at 0x%x so_rcv at 0x%x\n", &so->so_snd, &so->so_rcv);
+               dump_mbuf(so->so_snd.sb_mb, "so_snd at detach ");
+               printf("about to call LL detach, nlproto 0x%x, nl_detach 0x%x\n",
+                               tpcb->tp_nlproto, tpcb->tp_nlproto->nlp_pcbdetach);
+       ENDDEBUG
+
+       if ((tpcb->tp_nlproto->nlp_pcbdetach) (
+                               (struct inpcb *)so->so_pcb) /* does an sofree(so) */ < 0 ) {
+#ifdef         ARGO_DEBUG
+               printf("tp: nl_detach failed: tpcb 0x%x so 0x%x\n", tpcb, so);
+#endif         ARGO_DEBUG
+       }
+               
+       IFDEBUG(D_CONN)
+               printf("after xxx_pcbdetach\n");
+       ENDDEBUG
+
+       if( tpcb->tp_refp->tpr_state == REF_OPENING ) {
+               /* no connection existed here so no reference timer will be called */
+               IFDEBUG(D_CONN)
+                       printf("SETTING ref %d, 0x%x to REF_FREE\n", tpcb->tp_lref,
+                       tpcb->tp_refp - &tp_ref[0]);
+               ENDDEBUG
+
+               tp_freeref(tpcb->tp_refp);
+       }
+
+       so->so_tpcb = (caddr_t)0;
+
+       /* 
+        * Get rid of the cluster mbuf allocated for performance measurements, if
+        * there is one.  Note that tpcb->tp_perf_on says nothing about whether or 
+        * not a cluster mbuf was allocated, so you have to check for a pointer 
+        * to one (that is, we need the TP_PERF_MEASs around the following section 
+        * of code, not the IFPERFs)
+        */
+#ifdef TP_PERF_MEAS
+       if( tpcb->tp_p_meas != (struct tp_pmeas *)0 ) {
+               register struct mbuf *n;
+
+               n = MTOCL((struct mbuf *)(tpcb->tp_p_meas));
+               IFDEBUG(D_PERF_MEAS)
+                       printf("freeing tp_p_meas 0x%x  ", tpcb->tp_p_meas);
+                       printf("n = 0x%x\n", n);
+               ENDDEBUG
+               if (--mclrefcnt[mtocl(n)] == 0) { 
+                       n->m_next = mclfree;
+                       mclfree = n;
+                       mbstat.m_clfree++;
+               } 
+       }
+#endif TP_PERF_MEAS
+
+       IFDEBUG(D_CONN)
+               printf(
+"end of detach, NOT single, tpcb 0x%x, dtom(tpcb) 0x%x tp_aux 0x%x dtom(aux) 0x%x\n",
+                tpcb, dtom(tpcb), tpcb->tp_aux, dtom(tpcb->tp_aux));
+       ENDDEBUG
+       m_free(dtom(tpcb->tp_aux)); 
+       m_free(dtom(tpcb)); 
+}