#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/tp_param.h>
#include <netiso/tp_timer.h>
#include <netiso/tp_stat.h>
#include <netiso/tp_seq.h>
#include <netiso/tp_ip.h>
#include <netiso/tp_pcb.h>
#include <netiso/argo_debug.h>
#include <netiso/tp_trace.h>
#include <netiso/tp_meas.h>
#include <netiso/iso.h>
#include <netiso/iso_errno.h>
int tp_attach(), tp_driver(), tp_pcbbind();
int TNew;
int TPNagle1, TPNagle2;
struct tp_pcb *tp_listeners, *tp_intercepts;
#ifdef ARGO_DEBUG
void
dump_mbuf(n, str)
struct mbuf *n;
char *str;
{
struct mbuf *nextrecord;
printf("dump %s\n", str);
if (n == MNULL) {
printf("EMPTY:\n");
return;
}
while (n) {
nextrecord = n->m_act;
printf("RECORD:\n");
while (n) {
printf("%x : Len %x Data %x A %x Nx %x Tp %x\n",
n, n->m_len, n->m_data, n->m_act, n->m_next, n->m_type);
#ifdef notdef
{
register char *p = mtod(n, char *);
register int i;
printf("data: ");
for (i = 0; i < n->m_len; i++) {
if (i%8 == 0)
printf("\n");
printf("0x%x ", *(p+i));
}
printf("\n");
}
#endif
if (n->m_next == n) {
printf("LOOP!\n");
return;
}
n = n->m_next;
}
n = nextrecord;
}
printf("\n");
}
#endif
tp_rcvoob(tpcb, so, m, outflags, inflags)
struct tp_pcb *tpcb;
register struct socket *so;
register struct mbuf *m;
int *outflags;
int inflags;
{
register struct mbuf *n;
register struct sockbuf *sb = &so->so_rcv;
struct tp_event E;
int error = 0;
register struct mbuf **nn;
IFDEBUG(D_XPD)
printf("PRU_RCVOOB, sostate 0x%x\n", so->so_state);
ENDDEBUG
if (m == MNULL)
return ENOBUFS;
restart:
if ((((so->so_state & SS_ISCONNECTED) == 0)
|| (so->so_state & SS_ISDISCONNECTING) != 0) &&
(so->so_proto->pr_flags & PR_CONNREQUIRED)) {
return ENOTCONN;
}
sblock(sb, M_WAIT);
for (nn = &sb->sb_mb; n = *nn; nn = &n->m_act)
if (n->m_type == MT_OOBDATA)
break;
if (n == 0) {
IFDEBUG(D_XPD)
printf("RCVOOB: empty queue!\n");
ENDDEBUG
sbunlock(sb);
if (so->so_state & SS_NBIO) {
return EWOULDBLOCK;
}
sbwait(sb);
goto restart;
}
m->m_len = 0;
while (n != MNULL) {
m->m_len += n->m_len;
bcopy(mtod(n, caddr_t), mtod(m, caddr_t), (unsigned)n->m_len);
m->m_data += n->m_len;
n = n->m_next;
}
m->m_data = m->m_dat;
m->m_flags |= M_EOR;
IFDEBUG(D_XPD)
printf("tp_rcvoob: xpdlen 0x%x\n", m->m_len);
dump_mbuf(so->so_rcv.sb_mb, "RCVOOB: Rcv socketbuf");
dump_mbuf(sb->sb_mb, "RCVOOB: Xrcv socketbuf");
ENDDEBUG
if ((inflags & MSG_PEEK) == 0) {
n = *nn;
*nn = n->m_act;
for (; n; n = m_free(n))
sbfree(sb, n);
}
release:
sbunlock(sb);
IFTRACE(D_XPD)
tptraceTPCB(TPPTmisc, "PRU_RCVOOB @ release sb_cc m_len",
tpcb->tp_Xrcv.sb_cc, m->m_len, 0, 0);
ENDTRACE
if (error == 0)
error = DoEvent(T_USR_Xrcvd);
return error;
}
tp_sendoob(tpcb, so, xdata, outflags)
struct tp_pcb *tpcb;
register struct socket *so;
register struct mbuf *xdata;
int *outflags;
{
register struct sockbuf *sb = &(tpcb->tp_Xsnd);
register struct mbuf *xmark;
register int len=0;
struct tp_event E;
IFDEBUG(D_XPD)
printf("tp_sendoob:");
if (xdata)
printf("xdata len 0x%x\n", xdata->m_len);
ENDDEBUG
if (sb->sb_mb) {
if (so->so_state & SS_NBIO) {
return EWOULDBLOCK;
}
while (sb->sb_mb) {
sbunlock(&so->so_snd);
sbwait(&so->so_snd);
sblock(&so->so_snd, M_WAIT);
}
}
if (xdata == (struct mbuf *)0) {
MGETHDR(xdata, M_WAIT, MT_OOBDATA);
if (xdata == NULL) {
return ENOBUFS;
}
xdata->m_len = 0;
xdata->m_pkthdr.len = 0;
}
IFDEBUG(D_XPD)
printf("tp_sendoob 1:");
if (xdata)
printf("xdata len 0x%x\n", xdata->m_len);
ENDDEBUG
xmark = xdata;
while (xmark) {
len += xmark->m_len;
xmark = xmark->m_next;
}
if (len > TP_MAX_XPD_DATA) {
return EMSGSIZE;
}
IFDEBUG(D_XPD)
printf("tp_sendoob 2:");
if (xdata)
printf("xdata len 0x%x\n", len);
ENDDEBUG
IFTRACE(D_XPD)
tptraceTPCB(TPPTmisc, "XPD mark m_next ", xdata->m_next, 0, 0, 0);
ENDTRACE
sbappendrecord(sb, xdata);
IFDEBUG(D_XPD)
printf("tp_sendoob len 0x%x\n", len);
dump_mbuf(so->so_snd.sb_mb, "XPD request Regular sndbuf:");
dump_mbuf(tpcb->tp_Xsnd.sb_mb, "XPD request Xsndbuf:");
ENDDEBUG
return DoEvent(T_XPD_req);
}
ProtoHook
tp_usrreq(so, req, m, nam, controlp)
struct socket *so;
u_int req;
struct mbuf *m, *nam, *controlp;
{
register struct tp_pcb *tpcb = sototpcb(so);
int s = splnet();
int error = 0;
int flags, *outflags = &flags;
u_long eotsdu = 0;
struct tp_event E;
IFDEBUG(D_REQUEST)
printf("usrreq(0x%x,%d,0x%x,0x%x,0x%x)\n",so,req,m,nam,outflags);
if (so->so_error)
printf("WARNING!!! so->so_error is 0x%x\n", so->so_error);
ENDDEBUG
IFTRACE(D_REQUEST)
tptraceTPCB(TPPTusrreq, "req so m state [", req, so, m,
tpcb?tpcb->tp_state:0);
ENDTRACE
if ((u_int)tpcb == 0 && req != PRU_ATTACH) {
IFTRACE(D_REQUEST)
tptraceTPCB(TPPTusrreq, "req failed NO TPCB[", 0, 0, 0, 0);
ENDTRACE
splx(s);
return ENOTCONN;
}
switch (req) {
case PRU_ATTACH:
if (tpcb) {
error = EISCONN;
} else if ((error = tp_attach(so, (int)nam)) == 0)
tpcb = sototpcb(so);
break;
case PRU_ABORT:
if (tpcb->tp_state == TP_OPEN || tpcb->tp_state == TP_CONFIRMING) {
E.ATTR(T_DISC_req).e_reason = E_TP_NO_SESSION;
error = DoEvent(T_DISC_req);
break;
}
case PRU_DETACH:
error = DoEvent(T_DETACH);
if (tpcb->tp_state == TP_CLOSED) {
if (tpcb->tp_notdetached) {
IFDEBUG(D_CONN)
printf("PRU_DETACH: not detached\n");
ENDDEBUG
tp_detach(tpcb);
}
FREE((caddr_t)tpcb, M_PCB);
tpcb = 0;
}
break;
case PRU_SHUTDOWN:
case PRU_DISCONNECT:
E.ATTR(T_DISC_req).e_reason = E_TP_NORMAL_DISC;
error = DoEvent(T_DISC_req);
break;
case PRU_BIND:
error = tp_pcbbind(tpcb, nam);
break;
case PRU_LISTEN:
if (tpcb->tp_state != TP_CLOSED || tpcb->tp_lsuffixlen == 0 ||
tpcb->tp_next == 0)
error = EINVAL;
else {
register struct tp_pcb **tt;
remque(tpcb);
tpcb->tp_next = tpcb->tp_prev = tpcb;
for (tt = &tp_listeners; *tt; tt = &((*tt)->tp_nextlisten))
if ((*tt)->tp_lsuffixlen)
break;
tpcb->tp_nextlisten = *tt;
*tt = tpcb;
error = DoEvent(T_LISTEN_req);
}
break;
case PRU_CONNECT2:
error = EOPNOTSUPP;
break;
case PRU_CONNECT:
IFTRACE(D_CONN)
tptraceTPCB(TPPTmisc,
"PRU_CONNECT: so 0x%x *SHORT_LSUFXP(tpcb) 0x%x lsuflen 0x%x, class 0x%x",
tpcb->tp_sock, *SHORT_LSUFXP(tpcb), tpcb->tp_lsuffixlen,
tpcb->tp_class);
ENDTRACE
IFDEBUG(D_CONN)
printf("PRU_CONNECT: so *SHORT_LSUFXP(tpcb) 0x%x lsuflen 0x%x, class 0x%x",
tpcb->tp_sock, *SHORT_LSUFXP(tpcb), tpcb->tp_lsuffixlen,
tpcb->tp_class);
ENDDEBUG
if (tpcb->tp_lsuffixlen == 0) {
if (error = tp_pcbbind(tpcb, MNULL)) {
IFDEBUG(D_CONN)
printf("pcbbind returns error 0x%x\n", error);
ENDDEBUG
break;
}
}
IFDEBUG(D_CONN)
printf("isop 0x%x isop->isop_socket offset 12 :\n", tpcb->tp_npcb);
dump_buf(tpcb->tp_npcb, 16);
ENDDEBUG
if (error = tp_route_to(nam, tpcb, 0))
break;
IFDEBUG(D_CONN)
printf(
"PRU_CONNECT after tpcb 0x%x so 0x%x npcb 0x%x flags 0x%x\n",
tpcb, so, tpcb->tp_npcb, tpcb->tp_flags);
printf("isop 0x%x isop->isop_socket offset 12 :\n", tpcb->tp_npcb);
dump_buf(tpcb->tp_npcb, 16);
ENDDEBUG
if (tpcb->tp_fsuffixlen == 0) {
(tpcb->tp_nlproto->nlp_getsufx)(tpcb->tp_npcb, &tpcb->tp_fsuffixlen,
tpcb->tp_fsuffix, TP_FOREIGN);
}
if (tpcb->tp_state == TP_CLOSED) {
soisconnecting(so);
error = DoEvent(T_CONN_req);
} else {
(tpcb->tp_nlproto->nlp_pcbdisc)(tpcb->tp_npcb);
error = EISCONN;
}
IFPERF(tpcb)
u_int lsufx, fsufx;
lsufx = *(u_short *)(tpcb->tp_lsuffix);
fsufx = *(u_short *)(tpcb->tp_fsuffix);
tpmeas(tpcb->tp_lref,
TPtime_open | (tpcb->tp_xtd_format << 4),
&time, lsufx, fsufx, tpcb->tp_fref);
ENDPERF
break;
case PRU_ACCEPT:
(tpcb->tp_nlproto->nlp_getnetaddr)(tpcb->tp_npcb, nam, TP_FOREIGN);
IFDEBUG(D_REQUEST)
printf("ACCEPT PEERADDDR:");
dump_buf(mtod(nam, char *), nam->m_len);
ENDDEBUG
IFPERF(tpcb)
u_int lsufx, fsufx;
lsufx = *(u_short *)(tpcb->tp_lsuffix);
fsufx = *(u_short *)(tpcb->tp_fsuffix);
tpmeas(tpcb->tp_lref, TPtime_open,
&time, lsufx, fsufx, tpcb->tp_fref);
ENDPERF
break;
case PRU_RCVD:
if (so->so_state & SS_ISCONFIRMING) {
if (tpcb->tp_state == TP_CONFIRMING)
error = tp_confirm(tpcb);
break;
}
IFTRACE(D_DATA)
tptraceTPCB(TPPTmisc,
"RCVD BF: lcredit sent_lcdt cc hiwat \n",
tpcb->tp_lcredit, tpcb->tp_sent_lcdt,
so->so_rcv.sb_cc, so->so_rcv.sb_hiwat);
LOCAL_CREDIT(tpcb);
tptraceTPCB(TPPTmisc,
"PRU_RCVD AF sbspace lcredit hiwat cc",
sbspace(&so->so_rcv), tpcb->tp_lcredit,
so->so_rcv.sb_cc, so->so_rcv.sb_hiwat);
ENDTRACE
IFDEBUG(D_REQUEST)
printf("RCVD: cc %d space %d hiwat %d\n",
so->so_rcv.sb_cc, sbspace(&so->so_rcv),
so->so_rcv.sb_hiwat);
ENDDEBUG
if (((int)nam) & MSG_OOB)
error = DoEvent(T_USR_Xrcvd);
else
error = DoEvent(T_USR_rcvd);
break;
case PRU_RCVOOB:
if ((so->so_state & SS_ISCONNECTED) == 0) {
error = ENOTCONN;
break;
}
if (! tpcb->tp_xpd_service) {
error = EOPNOTSUPP;
break;
}
error = tp_rcvoob(tpcb, so, m, outflags, (int)nam);
break;
case PRU_SEND:
case PRU_SENDOOB:
if (controlp) {
error = tp_snd_control(controlp, so, &m);
controlp = NULL;
if (error)
break;
}
if ((so->so_state & SS_ISCONFIRMING) &&
(tpcb->tp_state == TP_CONFIRMING) &&
(error = tp_confirm(tpcb)))
break;
if (req == PRU_SENDOOB) {
error = (tpcb->tp_xpd_service == 0) ?
EOPNOTSUPP : tp_sendoob(tpcb, so, m, outflags);
break;
}
if (m == 0)
break;
if (m->m_flags & M_EOR) {
eotsdu = 1;
m->m_flags &= ~M_EOR;
}
if (eotsdu == 0 && m->m_pkthdr.len == 0)
break;
if (tpcb->tp_state != TP_AKWAIT && tpcb->tp_state != TP_OPEN) {
error = ENOTCONN;
break;
}
{
int totlen = m->m_pkthdr.len;
struct sockbuf *sb = &so->so_snd;
IFPERF(tpcb)
PStat(tpcb, Nb_from_sess) += totlen;
tpmeas(tpcb->tp_lref, TPtime_from_session, 0, 0,
PStat(tpcb, Nb_from_sess), totlen);
ENDPERF
IFDEBUG(D_SYSCALL)
printf(
"PRU_SEND: eot %d before sbappend 0x%x len 0x%x to sb @ 0x%x\n",
eotsdu, m, totlen, sb);
dump_mbuf(sb->sb_mb, "so_snd.sb_mb");
dump_mbuf(m, "m : to be added");
ENDDEBUG
tp_packetize(tpcb, m, eotsdu);
IFDEBUG(D_SYSCALL)
printf("PRU_SEND: eot %d after sbappend 0x%x\n", eotsdu, m);
dump_mbuf(sb->sb_mb, "so_snd.sb_mb");
ENDDEBUG
if (tpcb->tp_state == TP_OPEN)
error = DoEvent(T_DATA_req);
IFDEBUG(D_SYSCALL)
printf("PRU_SEND: after driver error 0x%x \n",error);
printf("so_snd 0x%x cc 0t%d mbcnt 0t%d\n",
sb, sb->sb_cc, sb->sb_mbcnt);
dump_mbuf(sb->sb_mb, "so_snd.sb_mb after driver");
ENDDEBUG
}
break;
case PRU_SOCKADDR:
(tpcb->tp_nlproto->nlp_getnetaddr)(tpcb->tp_npcb, nam, TP_LOCAL);
break;
case PRU_PEERADDR:
(tpcb->tp_nlproto->nlp_getnetaddr)(tpcb->tp_npcb, nam, TP_FOREIGN);
break;
case PRU_CONTROL:
error = EOPNOTSUPP;
break;
case PRU_PROTOSEND:
case PRU_PROTORCV:
case PRU_SENSE:
case PRU_SLOWTIMO:
case PRU_FASTTIMO:
error = EOPNOTSUPP;
break;
default:
#ifdef ARGO_DEBUG
printf("tp_usrreq UNKNOWN PRU %d\n", req);
#endif
error = EOPNOTSUPP;
}
IFDEBUG(D_REQUEST)
printf("%s, so 0x%x, tpcb 0x%x, error %d, state %d\n",
"returning from tp_usrreq", so, tpcb, error,
tpcb ? tpcb->tp_state : 0);
ENDDEBUG
IFTRACE(D_REQUEST)
tptraceTPCB(TPPTusrreq, "END req so m state [", req, so, m,
tpcb ? tpcb->tp_state : 0);
ENDTRACE
if (controlp) {
m_freem(controlp);
printf("control data unexpectedly retained in tp_usrreq()");
}
splx(s);
return error;
}
tp_ltrace(so, uio)
struct socket *so;
struct uio *uio;
{
IFTRACE(D_DATA)
register struct tp_pcb *tpcb = sototpcb(so);
if (tpcb) {
tptraceTPCB(TPPTmisc, "sosend so resid iovcnt", so,
uio->uio_resid, uio->uio_iovcnt, 0);
}
ENDTRACE
}
tp_confirm(tpcb)
register struct tp_pcb *tpcb;
{
struct tp_event E;
if (tpcb->tp_state == TP_CONFIRMING)
return DoEvent(T_ACPT_req);
printf("Tp confirm called when not confirming; tpcb 0x%x, state 0x%x\n",
tpcb, tpcb->tp_state);
return 0;
}
tp_snd_control(m, so, data)
struct mbuf *m;
struct socket *so;
register struct mbuf **data;
{
register struct cmsghdr *ch;
int error = 0;
if (m && m->m_len) {
ch = mtod(m, struct cmsghdr *);
m->m_len -= sizeof (*ch);
m->m_data += sizeof (*ch);
error = tp_ctloutput(PRCO_SETOPT,
so, ch->cmsg_level, ch->cmsg_type, &m);
if (ch->cmsg_type == TPOPT_DISC_DATA) {
if (data && *data) {
m_freem(*data);
*data = 0;
}
error = tp_usrreq(so, PRU_DISCONNECT, (struct mbuf *)0,
(caddr_t)0, (struct mbuf *)0);
}
}
if (m)
m_freem(m);
return error;
}