#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <netiso/argo_debug.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/tp_tpdu.h>
#include <netiso/tp_trace.h>
#include <netiso/tp_meas.h>
#include <netiso/tp_seq.h>
#include <netiso/tp_clnp.h>
struct tp_conn_param tp_conn_param[] = {
{
TP_NRETRANS,
20,
20,
20,
40,
80,
240,
10,
600,
360,
(short) 100,
(short) TP_SOCKBUFSIZE,
TP_TPDUSIZE,
TPACK_WINDOW,
TPRX_USE_CW | TPRX_FASTSTART,
TP_CLASS_4 | TP_CLASS_0,
1,
1,
1,
0,
0,
0,
1,
0,
ISO_CLNS,
},
{
TP_NRETRANS,
20,
20,
20,
40,
80,
240,
10,
600,
360,
(short) 100,
(short) TP_SOCKBUFSIZE,
TP_TPDUSIZE,
TPACK_WINDOW,
TPRX_USE_CW | TPRX_FASTSTART,
TP_CLASS_4,
1,
1,
1,
0,
0,
0,
1,
0,
IN_CLNS,
},
{
TP_NRETRANS,
0,
40,
0,
0,
360,
0,
0,
600,
0,
(short) 100,
(short) TP0_SOCKBUFSIZE,
TP0_TPDUSIZE,
0,
0,
TP_CLASS_0,
0,
0,
0,
0,
0,
0,
0,
0,
ISO_CONS,
},
{
TP_NRETRANS,
40,
40,
80,
120,
360,
360,
20,
600,
480,
(short) 100,
(short) TP0_SOCKBUFSIZE,
TP0_TPDUSIZE,
TPACK_WINDOW,
TPRX_USE_CW ,
TP_CLASS_4 | TP_CLASS_0,
0,
1,
1,
0,
0,
0,
0,
0,
ISO_COSNS,
},
};
#if INET
int in_putnetaddr();
int in_getnetaddr();
int in_cmpnetaddr();
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
#if ISO
int iso_putnetaddr();
int iso_getnetaddr();
int iso_cmpnetaddr();
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
#if TPCONS
int iso_putnetaddr();
int iso_getnetaddr();
int iso_cmpnetaddr();
int iso_putsufx();
int iso_getsufx();
int iso_recycle_tsuffix();
int iso_pcbbind();
int tpcons_pcbconnect();
int tpclnp_mtu();
int iso_pcbdisconnect();
int iso_pcbdetach();
int iso_pcballoc();
int tpcons_output();
struct isopcb tp_isopcb;
#endif
struct nl_protosw nl_protosw[] = {
#if ISO
{ AF_ISO, iso_putnetaddr, iso_getnetaddr, iso_cmpnetaddr,
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,
},
#else
{ 0 },
#endif
#if INET
{ AF_INET, in_putnetaddr, in_getnetaddr, in_cmpnetaddr,
in_putsufx, in_getsufx,
in_recycle_tsuffix,
tpip_mtu, in_pcbbind, in_pcbconnect,
in_pcbdisconnect, in_pcbdetach,
in_pcballoc,
tpip_output, tpip_output_dg, NULL,
(caddr_t) &tp_inpcb,
},
#else
{ 0 },
#endif
#if defined(ISO) && defined(TPCONS)
{ AF_ISO, iso_putnetaddr, iso_getnetaddr, iso_cmpnetaddr,
iso_putsufx, iso_getsufx,
iso_recycle_tsuffix,
tpclnp_mtu, iso_pcbbind, tpcons_pcbconnect,
iso_pcbdisconnect, iso_pcbdetach,
iso_pcballoc,
tpcons_output, tpcons_output, iso_nlctloutput,
(caddr_t) &tp_isopcb,
},
#else
{ 0 },
#endif
{ 0 }
};
u_long tp_sendspace = 1024 * 4;
u_long tp_recvspace = 1024 * 4;
int
tp_init()
{
static int init_done=0;
void tp_timerinit();
if (init_done++)
return 0;
tp_inpcb.inp_next = tp_inpcb.inp_prev = &tp_inpcb;
tp_isopcb.isop_next = tp_isopcb.isop_prev = &tp_isopcb;
tp_start_win = 2;
tp_timerinit();
bzero((caddr_t)&tp_stat, sizeof(struct tp_stat));
return 0;
}
void
tp_soisdisconnecting(so)
register struct socket *so;
{
soisdisconnecting(so);
so->so_state &= ~SS_CANTSENDMORE;
IFPERF(sototpcb(so))
register struct tp_pcb *tpcb = sototpcb(so);
u_int fsufx, lsufx;
bcopy ((caddr_t)tpcb->tp_fsuffix, (caddr_t)&fsufx, sizeof(u_int) );
bcopy ((caddr_t)tpcb->tp_lsuffix, (caddr_t)&lsufx, sizeof(u_int) );
tpmeas(tpcb->tp_lref, TPtime_close, &time, fsufx, lsufx, tpcb->tp_fref);
tpcb->tp_perf_on = 0;
ENDPERF
}
void
tp_soisdisconnected(tpcb)
register struct tp_pcb *tpcb;
{
register struct socket *so = tpcb->tp_sock;
soisdisconnecting(so);
so->so_state &= ~SS_CANTSENDMORE;
IFPERF(tpcb)
register struct tp_pcb *ttpcb = sototpcb(so);
u_int fsufx, lsufx;
bcopy ((caddr_t)ttpcb->tp_fsuffix, (caddr_t)&fsufx, sizeof(u_int) );
bcopy ((caddr_t)ttpcb->tp_lsuffix, (caddr_t)&lsufx, sizeof(u_int) );
tpmeas(ttpcb->tp_lref, TPtime_close,
&time, &lsufx, &fsufx, ttpcb->tp_fref);
tpcb->tp_perf_on = 0;
ENDPERF
tpcb->tp_refstate = REF_FROZEN;
tp_recycle_tsuffix(tpcb);
tp_etimeout(tpcb, TM_reference, (int)tpcb->tp_refer_ticks);
}
void
tp_freeref(n)
RefNum n;
{
register struct tp_ref *r = tp_ref + n;
register struct tp_pcb *tpcb;
tpcb = r->tpr_pcb;
IFDEBUG(D_TIMER)
printf("tp_freeref called for ref %d pcb %x maxrefopen %d\n",
n, tpcb, tp_refinfo.tpr_maxopen);
ENDDEBUG
IFTRACE(D_TIMER)
tptrace(TPPTmisc, "tp_freeref ref maxrefopen pcb",
n, tp_refinfo.tpr_maxopen, tpcb, 0);
ENDTRACE
if (tpcb == 0)
return;
IFDEBUG(D_CONN)
printf("tp_freeref: CLEARING tpr_pcb 0x%x\n", tpcb);
ENDDEBUG
r->tpr_pcb = (struct tp_pcb *)0;
tpcb->tp_refstate = REF_FREE;
for (r = tp_ref + tp_refinfo.tpr_maxopen; r > tp_ref; r--)
if (r->tpr_pcb)
break;
tp_refinfo.tpr_maxopen = r - tp_ref;
tp_refinfo.tpr_numopen--;
IFDEBUG(D_TIMER)
printf("tp_freeref ends w/ maxrefopen %d\n", tp_refinfo.tpr_maxopen);
ENDDEBUG
}
u_long
tp_getref(tpcb)
register struct tp_pcb *tpcb;
{
register struct tp_ref *r, *rlim;
register int i;
caddr_t obase;
unsigned size;
if (++tp_refinfo.tpr_numopen < tp_refinfo.tpr_size)
for (r = tp_refinfo.tpr_base, rlim = r + tp_refinfo.tpr_size;
++r < rlim; )
if (r->tpr_pcb == 0)
goto got_one;
obase = (caddr_t)tp_refinfo.tpr_base;
size = tp_refinfo.tpr_size * sizeof(struct tp_ref);
MALLOC(r, struct tp_ref *, size + size, M_PCB, M_NOWAIT);
if (r == 0)
return (--tp_refinfo.tpr_numopen, TP_ENOREF);
tp_refinfo.tpr_base = tp_ref = r;
tp_refinfo.tpr_size *= 2;
bcopy(obase, (caddr_t)r, size);
FREE(obase, M_PCB);
r = (struct tp_ref *)(size + (caddr_t)r);
bzero((caddr_t)r, size);
got_one:
r->tpr_pcb = tpcb;
tpcb->tp_refstate = REF_OPENING;
i = r - tp_refinfo.tpr_base;
if (tp_refinfo.tpr_maxopen < i)
tp_refinfo.tpr_maxopen = i;
return (u_long)i;
}
tp_set_npcb(tpcb)
register struct tp_pcb *tpcb;
{
register struct socket *so = tpcb->tp_sock;
int error;
if (tpcb->tp_nlproto && tpcb->tp_npcb) {
short so_state = so->so_state;
so->so_state &= ~SS_NOFDREF;
tpcb->tp_nlproto->nlp_pcbdetach(tpcb->tp_npcb);
so->so_state = so_state;
}
tpcb->tp_nlproto = &nl_protosw[tpcb->tp_netservice];
error = tpcb->tp_nlproto->nlp_pcballoc(so, tpcb->tp_nlproto->nlp_pcblist);
tpcb->tp_npcb = so->so_pcb;
so->so_pcb = (caddr_t)tpcb;
return (error);
}
tp_attach(so, protocol)
struct socket *so;
int protocol;
{
register struct tp_pcb *tpcb;
int error = 0;
int dom = so->so_proto->pr_domain->dom_family;
u_long lref;
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 (so->so_pcb != NULL) {
return EISCONN;
}
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0)
error = soreserve(so, tp_sendspace, tp_recvspace);
if (error)
goto bad2;
MALLOC(tpcb, struct tp_pcb *, sizeof(*tpcb), M_PCB, M_NOWAIT);
if (tpcb == NULL) {
error = ENOBUFS;
goto bad2;
}
bzero( (caddr_t)tpcb, sizeof (struct tp_pcb) );
if ( ((lref = tp_getref(tpcb)) & TP_ENOREF) != 0 ) {
error = ETOOMANYREFS;
goto bad3;
}
tpcb->tp_lref = lref;
tpcb->tp_sock = so;
tpcb->tp_domain = dom;
tpcb->tp_rhiwat = so->so_rcv.sb_hiwat;
if (protocol && protocol<ISOPROTO_TP4) {
tpcb->tp_netservice = ISO_CONS;
tpcb->tp_snduna = (SeqNum) -1;
} else {
tpcb->tp_netservice = (dom== AF_INET)?IN_CLNS:ISO_CLNS;
}
tpcb->_tp_param = tp_conn_param[tpcb->tp_netservice];
tpcb->tp_state = TP_CLOSED;
tpcb->tp_vers = TP_VERSION;
tpcb->tp_notdetached = 1;
tpcb->tp_cong_win =
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;
if ( error = tp_set_npcb(tpcb))
goto bad4;
ASSERT( tpcb->tp_nlproto->nlp_afamily == tpcb->tp_domain);
if( dom == AF_INET )
sotoinpcb(so)->inp_ppcb = (caddr_t) tpcb;
return 0;
bad4:
IFDEBUG(D_CONN)
printf("BAD4 in tp_attach, so 0x%x\n", so);
ENDDEBUG
tp_freeref(tpcb->tp_lref);
bad3:
IFDEBUG(D_CONN)
printf("BAD3 in tp_attach, so 0x%x\n", so);
ENDDEBUG
FREE((caddr_t)tpcb, M_PCB);
bad2:
IFDEBUG(D_CONN)
printf("BAD2 in tp_attach, so 0x%x\n", so);
ENDDEBUG
so->so_pcb = 0;
IFDEBUG(D_CONN)
printf("BAD in tp_attach, so 0x%x\n", so);
ENDDEBUG
return error;
}
void
tp_detach(tpcb)
register struct tp_pcb *tpcb;
{
void tp_freeref(), tp_rsyflush();
register struct socket *so = tpcb->tp_sock;
IFDEBUG(D_CONN)
printf("tp_detach(tpcb 0x%x, so 0x%x)\n",
tpcb,so);
ENDDEBUG
IFTRACE(D_CONN)
tptraceTPCB(TPPTmisc, "tp_detach tpcb so lsufx",
tpcb, so, *(u_short *)(tpcb->tp_lsuffix), 0);
ENDTRACE
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_Xsnd.sb_mb) {
printf("Unsent Xdata on detach; would panic");
sbflush(&tpcb->tp_Xsnd);
}
if (tpcb->tp_ucddata)
m_freem(tpcb->tp_ucddata);
IFDEBUG(D_CONN)
printf("reassembly info cnt %d rsyq 0x%x\n",
tpcb->tp_rsycnt, tpcb->tp_rsyq);
ENDDEBUG
if (tpcb->tp_rsyq)
tp_rsyflush(tpcb);
if (tpcb->tp_next) {
remque(tpcb);
tpcb->tp_next = tpcb->tp_prev = 0;
}
tpcb->tp_notdetached = 0;
IFDEBUG(D_CONN)
printf("calling (...nlproto->...)(0x%x, so 0x%x)\n",
tpcb->tp_npcb, 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
(tpcb->tp_nlproto->nlp_pcbdetach)(tpcb->tp_npcb);
IFDEBUG(D_CONN)
printf("after xxx_pcbdetach\n");
ENDDEBUG
if (tpcb->tp_state == TP_LISTENING) {
register struct tp_pcb **tt;
for (tt = &tp_listeners; *tt; tt = &((*tt)->tp_nextlisten))
if (*tt == tpcb)
break;
if (*tt)
*tt = tpcb->tp_nextlisten;
else
printf("tp_detach from listen: should panic\n");
}
if (tpcb->tp_refstate == REF_OPENING ) {
IFDEBUG(D_CONN)
printf("SETTING ref %d to REF_FREE\n", tpcb->tp_lref);
ENDDEBUG
tp_freeref(tpcb->tp_lref);
}
#ifdef TP_PERF_MEAS
if (tpcb->tp_p_mbuf) {
register struct mbuf *m = tpcb->tp_p_mbuf;
struct mbuf *n;
IFDEBUG(D_PERF_MEAS)
printf("freeing tp_p_meas 0x%x ", tpcb->tp_p_meas);
ENDDEBUG
do {
MFREE(m, n);
m = n;
} while (n);
tpcb->tp_p_meas = 0;
tpcb->tp_p_mbuf = 0;
}
#endif
IFDEBUG(D_CONN)
printf( "end of detach, NOT single, tpcb 0x%x\n", tpcb);
ENDDEBUG
}
struct que {
struct tp_pcb *next;
struct tp_pcb *prev;
} tp_bound_pcbs =
{(struct tp_pcb *)&tp_bound_pcbs, (struct tp_pcb *)&tp_bound_pcbs};
u_short tp_unique;
tp_tselinuse(tlen, tsel, siso, reuseaddr)
caddr_t tsel;
register struct sockaddr_iso *siso;
{
struct tp_pcb *b = tp_bound_pcbs.next, *l = tp_listeners;
register struct tp_pcb *t;
for (;;) {
if (b != (struct tp_pcb *)&tp_bound_pcbs) {
t = b; b = t->tp_next;
} else if (l) {
t = l; l = t->tp_nextlisten;
} else
break;
if (tlen == t->tp_lsuffixlen && bcmp(tsel, t->tp_lsuffix, tlen) == 0) {
if (t->tp_flags & TPF_GENERAL_ADDR) {
if (siso == 0 || reuseaddr == 0)
return 1;
} else if (siso) {
if (siso->siso_family == t->tp_domain &&
t->tp_nlproto->nlp_cmpnetaddr(t->tp_npcb, siso, TP_LOCAL))
return 1;
} else if (reuseaddr == 0)
return 1;
}
}
return 0;
}
tp_pcbbind(tpcb, nam)
register struct tp_pcb *tpcb;
register struct mbuf *nam;
{
register struct sockaddr_iso *siso = 0;
int tlen = 0, wrapped = 0;
caddr_t tsel;
u_short tutil;
if (tpcb->tp_state != TP_CLOSED)
return (EINVAL);
if (nam) {
siso = mtod(nam, struct sockaddr_iso *);
switch (siso->siso_family) {
default:
return (EAFNOSUPPORT);
#if ISO
case AF_ISO:
tlen = siso->siso_tlen;
tsel = TSEL(siso);
if (siso->siso_nlen == 0)
siso = 0;
break;
#endif
#if INET
case AF_INET:
tsel = (caddr_t)&tutil;
if (tutil = ((struct sockaddr_in *)siso)->sin_port) {
tlen = 2;
}
if (((struct sockaddr_in *)siso)->sin_addr.s_addr == 0)
siso = 0;
}
#endif
}
if (tpcb->tp_lsuffixlen == 0) {
if (tlen) {
if (tp_tselinuse(tlen, tsel, siso,
tpcb->tp_sock->so_options & SO_REUSEADDR))
return (EINVAL);
} else {
for (tsel = (caddr_t)&tutil, tlen = 2;;){
if (tp_unique++ < ISO_PORT_RESERVED ||
tp_unique > ISO_PORT_USERRESERVED) {
if (wrapped++)
return ESRCH;
tp_unique = ISO_PORT_RESERVED;
}
tutil = htons(tp_unique);
if (tp_tselinuse(tlen, tsel, siso, 0) == 0)
break;
}
if (siso) switch (siso->siso_family) {
#if ISO
case AF_ISO:
bcopy(tsel, TSEL(siso), tlen);
siso->siso_tlen = tlen;
break;
#endif
#if INET
case AF_INET:
((struct sockaddr_in *)siso)->sin_port = tutil;
#endif
}
}
bcopy(tsel, tpcb->tp_lsuffix, (tpcb->tp_lsuffixlen = tlen));
insque(tpcb, &tp_bound_pcbs);
} else {
if (tlen || siso == 0)
return (EINVAL);
}
if (siso == 0) {
tpcb->tp_flags |= TPF_GENERAL_ADDR;
return (0);
}
return tpcb->tp_nlproto->nlp_pcbbind(tpcb->tp_npcb, nam);
}