#include <sys/param.h>
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strsubr.h>
#define _SUN_TPI_VERSION 2
#include <sys/tihdr.h>
#include <sys/timod.h>
#include <sys/suntpi.h>
#include <sys/debug.h>
#include <sys/strlog.h>
#include <sys/errno.h>
#include <sys/cred.h>
#include <sys/cmn_err.h>
#include <sys/kmem.h>
#include <sys/sysmacros.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/strsun.h>
#include <c2/audit.h>
#include <sys/conf.h>
#include <sys/modctl.h>
static struct streamtab timinfo;
static struct fmodsw fsw = {
"timod",
&timinfo,
D_MTQPAIR | D_MP,
};
static struct modlstrmod modlstrmod = {
&mod_strmodops, "transport interface str mod", &fsw
};
static struct modlinkage modlinkage = {
MODREV_1, &modlstrmod, NULL
};
static krwlock_t tim_list_rwlock;
struct tim_tim {
uint32_t tim_flags;
t_uscalar_t tim_backlog;
mblk_t *tim_iocsave;
t_scalar_t tim_mymaxlen;
t_scalar_t tim_mylen;
caddr_t tim_myname;
t_scalar_t tim_peermaxlen;
t_scalar_t tim_peerlen;
caddr_t tim_peername;
cred_t *tim_peercred;
mblk_t *tim_consave;
bufcall_id_t tim_wbufcid;
bufcall_id_t tim_rbufcid;
timeout_id_t tim_wtimoutid;
timeout_id_t tim_rtimoutid;
struct tim_tim *tim_next;
struct tim_tim **tim_ptpn;
t_uscalar_t tim_acceptor;
t_scalar_t tim_saved_prim;
timeout_id_t tim_tcap_timoutid;
tpi_provinfo_t *tim_provinfo;
kmutex_t tim_mutex;
pid_t tim_cpid;
};
#define PEEK_RDQ_EXPIND 0x0001
#define WAITIOCACK 0x0002
#define CLTS 0x0004
#define COTS 0x0008
#define CONNWAIT 0x0010
#define LOCORDREL 0x0020
#define REMORDREL 0x0040
#define NAMEPROC 0x0080
#define DO_MYNAME 0x0100
#define DO_PEERNAME 0x0200
#define TI_CAP_RECVD 0x0400
#define CAP_WANTS_INFO 0x0800
#define WAIT_IOCINFOACK 0x1000
#define WAIT_CONNRESACK 0x2000
#ifdef DEBUG
#define TILOG(msg, arg) tilog((msg), (arg))
#define TILOGP(msg, arg) tilogp((msg), (arg))
#else
#define TILOG(msg, arg)
#define TILOGP(msg, arg)
#endif
clock_t tim_tcap_wait = 2;
#define TIMWAIT (1*hz)
#define TIMIOCWAIT (200*hz/1000)
#define DONAME_FAIL 0
#define DONAME_DONE 1
#define DONAME_CONT 2
static int ti_doname(queue_t *, mblk_t *);
static int ti_expind_on_rdqueues(queue_t *);
static void tim_ioctl_send_reply(queue_t *, mblk_t *, mblk_t *);
static void tim_send_ioc_error_ack(queue_t *, struct tim_tim *, mblk_t *);
static void tim_tcap_timer(void *);
static void tim_tcap_genreply(queue_t *, struct tim_tim *);
static void tim_send_reply(queue_t *, mblk_t *, struct tim_tim *, t_scalar_t);
static void tim_answer_ti_sync(queue_t *, mblk_t *, struct tim_tim *,
mblk_t *, uint32_t);
static void tim_send_ioctl_tpi_msg(queue_t *, mblk_t *, struct tim_tim *,
struct iocblk *);
static void tim_clear_peer(struct tim_tim *);
int
_init(void)
{
int error;
rw_init(&tim_list_rwlock, NULL, RW_DRIVER, NULL);
error = mod_install(&modlinkage);
if (error != 0) {
rw_destroy(&tim_list_rwlock);
return (error);
}
return (0);
}
int
_fini(void)
{
int error;
error = mod_remove(&modlinkage);
if (error != 0)
return (error);
rw_destroy(&tim_list_rwlock);
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
#define TIM_HASH_SIZE 256
#ifdef _ILP32
#define TIM_HASH(id) (((uintptr_t)(id) >> 8) % TIM_HASH_SIZE)
#else
#define TIM_HASH(id) ((uintptr_t)(id) % TIM_HASH_SIZE)
#endif
static struct tim_tim *tim_hash[TIM_HASH_SIZE];
int tim_cnt = 0;
static void tilog(char *, t_scalar_t);
static void tilogp(char *, uintptr_t);
static mblk_t *tim_filladdr(queue_t *, mblk_t *, boolean_t);
static void tim_addlink(struct tim_tim *);
static void tim_dellink(struct tim_tim *);
static struct tim_tim *tim_findlink(t_uscalar_t);
static void tim_recover(queue_t *, mblk_t *, t_scalar_t);
static void tim_ioctl_retry(queue_t *);
int dotilog = 0;
#define TIMOD_ID 3
static int timodopen(queue_t *, dev_t *, int, int, cred_t *);
static int timodclose(queue_t *, int, cred_t *);
static int timodwput(queue_t *, mblk_t *);
static int timodrput(queue_t *, mblk_t *);
static int timodrsrv(queue_t *);
static int timodwsrv(queue_t *);
static int timodrproc(queue_t *, mblk_t *);
static int timodwproc(queue_t *, mblk_t *);
static struct module_info timod_info =
{TIMOD_ID, "timod", 0, INFPSZ, 512, 128};
static struct qinit timodrinit = {
timodrput,
timodrsrv,
timodopen,
timodclose,
nulldev,
&timod_info,
NULL
};
static struct qinit timodwinit = {
timodwput,
timodwsrv,
timodopen,
timodclose,
nulldev,
&timod_info,
NULL
};
static struct streamtab timinfo = { &timodrinit, &timodwinit, NULL, NULL };
static int
timodopen(
queue_t *q,
dev_t *devp,
int flag,
int sflag,
cred_t *crp)
{
struct tim_tim *tp;
struct stroptions *sop;
mblk_t *bp;
ASSERT(q != NULL);
if (q->q_ptr) {
return (0);
}
if ((bp = allocb(sizeof (struct stroptions), BPRI_MED)) == 0)
return (ENOMEM);
tp = kmem_zalloc(sizeof (struct tim_tim), KM_SLEEP);
tp->tim_cpid = -1;
tp->tim_saved_prim = -1;
mutex_init(&tp->tim_mutex, NULL, MUTEX_DEFAULT, NULL);
q->q_ptr = (caddr_t)tp;
WR(q)->q_ptr = (caddr_t)tp;
tilogp("timodopen: Allocated for tp %lx\n", (uintptr_t)tp);
tilogp("timodopen: Allocated for q %lx\n", (uintptr_t)q);
qprocson(q);
tp->tim_provinfo = tpi_findprov(q);
if (tp->tim_provinfo->tpi_myname != PI_YES) {
TILOG("timodopen: setting DO_MYNAME\n", 0);
tp->tim_flags |= DO_MYNAME;
}
if (tp->tim_provinfo->tpi_peername != PI_YES) {
TILOG("timodopen: setting DO_PEERNAME\n", 0);
tp->tim_flags |= DO_PEERNAME;
}
#ifdef _ILP32
{
queue_t *driverq;
driverq = WR(q);
while (SAMESTR(driverq))
driverq = driverq->q_next;
tp->tim_acceptor = (t_uscalar_t)RD(driverq);
}
#else
tp->tim_acceptor = (t_uscalar_t)getminor(*devp);
#endif
tim_addlink(tp);
bp->b_datap->db_type = M_SETOPTS;
bp->b_wptr += sizeof (struct stroptions);
sop = (struct stroptions *)bp->b_rptr;
sop->so_flags = SO_READOPT;
sop->so_readopt = RFLUSHPCPROT;
putnext(q, bp);
return (0);
}
static void
tim_timer(void *arg)
{
queue_t *q = arg;
struct tim_tim *tp = (struct tim_tim *)q->q_ptr;
ASSERT(tp);
if (q->q_flag & QREADR) {
ASSERT(tp->tim_rtimoutid);
tp->tim_rtimoutid = 0;
} else {
ASSERT(tp->tim_wtimoutid);
tp->tim_wtimoutid = 0;
}
enableok(q);
qenable(q);
}
static void
tim_buffer(void *arg)
{
queue_t *q = arg;
struct tim_tim *tp = (struct tim_tim *)q->q_ptr;
ASSERT(tp);
if (q->q_flag & QREADR) {
ASSERT(tp->tim_rbufcid);
tp->tim_rbufcid = 0;
} else {
ASSERT(tp->tim_wbufcid);
tp->tim_wbufcid = 0;
}
enableok(q);
qenable(q);
}
static int
timodclose(
queue_t *q,
int flag,
cred_t *crp)
{
struct tim_tim *tp;
mblk_t *mp;
mblk_t *nmp;
ASSERT(q != NULL);
tp = (struct tim_tim *)q->q_ptr;
q->q_ptr = NULL;
ASSERT(tp != NULL);
tilogp("timodclose: Entered for tp %lx\n", (uintptr_t)tp);
tilogp("timodclose: Entered for q %lx\n", (uintptr_t)q);
qprocsoff(q);
tim_dellink(tp);
if (tp->tim_wbufcid) {
qunbufcall(q, tp->tim_wbufcid);
tp->tim_wbufcid = 0;
}
if (tp->tim_rbufcid) {
qunbufcall(q, tp->tim_rbufcid);
tp->tim_rbufcid = 0;
}
if (tp->tim_wtimoutid) {
(void) quntimeout(q, tp->tim_wtimoutid);
tp->tim_wtimoutid = 0;
}
if (tp->tim_rtimoutid) {
(void) quntimeout(q, tp->tim_rtimoutid);
tp->tim_rtimoutid = 0;
}
if (tp->tim_tcap_timoutid != 0) {
(void) quntimeout(q, tp->tim_tcap_timoutid);
tp->tim_tcap_timoutid = 0;
}
if (tp->tim_iocsave != NULL)
freemsg(tp->tim_iocsave);
mp = tp->tim_consave;
while (mp) {
nmp = mp->b_next;
mp->b_next = NULL;
freemsg(mp);
mp = nmp;
}
ASSERT(tp->tim_mymaxlen >= 0);
if (tp->tim_mymaxlen != 0)
kmem_free(tp->tim_myname, (size_t)tp->tim_mymaxlen);
ASSERT(tp->tim_peermaxlen >= 0);
if (tp->tim_peermaxlen != 0)
kmem_free(tp->tim_peername, (size_t)tp->tim_peermaxlen);
q->q_ptr = WR(q)->q_ptr = NULL;
mutex_destroy(&tp->tim_mutex);
if (tp->tim_peercred != NULL)
crfree(tp->tim_peercred);
kmem_free(tp, sizeof (struct tim_tim));
return (0);
}
static int
timodrput(queue_t *q, mblk_t *mp)
{
union T_primitives *pptr;
if (q->q_first != 0 && mp->b_datap->db_type < QPCTL) {
(void) putq(q, mp);
return (0);
}
switch (mp->b_datap->db_type) {
case M_DATA:
if (bcanputnext(q, mp->b_band))
putnext(q, mp);
else
(void) putq(q, mp);
break;
case M_PROTO:
case M_PCPROTO:
if (MBLKL(mp) < sizeof (t_scalar_t)) {
if (mp->b_datap->db_type == M_PCPROTO ||
bcanputnext(q, mp->b_band)) {
putnext(q, mp);
} else {
(void) putq(q, mp);
}
break;
}
pptr = (union T_primitives *)mp->b_rptr;
switch (pptr->type) {
case T_EXDATA_IND:
case T_DATA_IND:
case T_UNITDATA_IND:
if (bcanputnext(q, mp->b_band))
putnext(q, mp);
else
(void) putq(q, mp);
break;
default:
(void) timodrproc(q, mp);
break;
}
break;
default:
(void) timodrproc(q, mp);
break;
}
return (0);
}
static int
timodrsrv(queue_t *q)
{
mblk_t *mp;
struct tim_tim *tp;
ASSERT(q != NULL);
tp = (struct tim_tim *)q->q_ptr;
if (!tp)
return (0);
while ((mp = getq(q)) != NULL) {
if (timodrproc(q, mp)) {
return (0);
}
}
return (0);
}
static void
timodprocessinfo(queue_t *q, struct tim_tim *tp, struct T_info_ack *tia)
{
TILOG("timodprocessinfo: strqset(%d)\n", tia->TIDU_size);
(void) strqset(q, QMAXPSZ, 0, tia->TIDU_size);
(void) strqset(OTHERQ(q), QMAXPSZ, 0, tia->TIDU_size);
if ((tia->SERV_type == T_COTS) || (tia->SERV_type == T_COTS_ORD))
tp->tim_flags = (tp->tim_flags & ~CLTS) | COTS;
else if (tia->SERV_type == T_CLTS)
tp->tim_flags = (tp->tim_flags & ~COTS) | CLTS;
}
static int
timodrproc(queue_t *q, mblk_t *mp)
{
uint32_t auditing = AU_AUDITING();
union T_primitives *pptr;
struct tim_tim *tp;
struct iocblk *iocbp;
mblk_t *nbp;
size_t blen;
tp = (struct tim_tim *)q->q_ptr;
switch (mp->b_datap->db_type) {
default:
putnext(q, mp);
break;
case M_ERROR:
TILOG("timodrproc: Got M_ERROR, flags = %x\n", tp->tim_flags);
if ((tp->tim_provinfo->tpi_capability == PI_DONTKNOW) &&
((tp->tim_flags & TI_CAP_RECVD) != 0)) {
PI_PROVLOCK(tp->tim_provinfo);
if (tp->tim_provinfo->tpi_capability == PI_DONTKNOW)
tp->tim_provinfo->tpi_capability = PI_NO;
PI_PROVUNLOCK(tp->tim_provinfo);
if (tp->tim_tcap_timoutid != 0) {
(void) quntimeout(q, tp->tim_tcap_timoutid);
tp->tim_tcap_timoutid = 0;
}
}
putnext(q, mp);
break;
case M_DATA:
if (!bcanputnext(q, mp->b_band)) {
(void) putbq(q, mp);
return (1);
}
putnext(q, mp);
break;
case M_PROTO:
case M_PCPROTO:
blen = MBLKL(mp);
if (blen < sizeof (t_scalar_t)) {
ASSERT(mp->b_datap->db_type == M_PROTO);
if (mp->b_datap->db_type == M_PROTO &&
!bcanputnext(q, mp->b_band)) {
(void) putbq(q, mp);
return (1);
}
putnext(q, mp);
break;
}
pptr = (union T_primitives *)mp->b_rptr;
switch (pptr->type) {
default:
if (auditing)
audit_sock(T_UNITDATA_IND, q, mp, TIMOD_ID);
putnext(q, mp);
break;
case T_ERROR_ACK:
mp->b_datap->db_type = M_PCPROTO;
if (blen < sizeof (struct T_error_ack)) {
putnext(q, mp);
break;
}
tilog("timodrproc: Got T_ERROR_ACK, flags = %x\n",
tp->tim_flags);
if ((tp->tim_flags & WAIT_CONNRESACK) &&
tp->tim_saved_prim == pptr->error_ack.ERROR_prim) {
tp->tim_flags &=
~(WAIT_CONNRESACK | WAITIOCACK);
freemsg(tp->tim_iocsave);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
putnext(q, mp);
} else if (tp->tim_flags & WAITIOCACK) {
tim_send_ioc_error_ack(q, tp, mp);
} else {
putnext(q, mp);
}
break;
case T_OK_ACK:
if (blen < sizeof (pptr->ok_ack)) {
mp->b_datap->db_type = M_PCPROTO;
putnext(q, mp);
break;
}
tilog("timodrproc: Got T_OK_ACK\n", 0);
if (pptr->ok_ack.CORRECT_prim == T_UNBIND_REQ)
tp->tim_mylen = 0;
if ((tp->tim_flags & WAIT_CONNRESACK) &&
tp->tim_saved_prim == pptr->ok_ack.CORRECT_prim) {
struct T_conn_res *resp;
struct T_conn_ind *indp;
struct tim_tim *ntp;
caddr_t ptr;
rw_enter(&tim_list_rwlock, RW_READER);
resp = (struct T_conn_res *)
tp->tim_iocsave->b_rptr;
ntp = tim_findlink(resp->ACCEPTOR_id);
if (ntp == NULL)
goto cresackout;
mutex_enter(&ntp->tim_mutex);
if (ntp->tim_peercred != NULL)
crfree(ntp->tim_peercred);
ntp->tim_peercred =
msg_getcred(tp->tim_iocsave->b_cont,
&ntp->tim_cpid);
if (ntp->tim_peercred != NULL)
crhold(ntp->tim_peercred);
if (!(ntp->tim_flags & DO_PEERNAME)) {
mutex_exit(&ntp->tim_mutex);
goto cresackout;
}
indp = (struct T_conn_ind *)
tp->tim_iocsave->b_cont->b_rptr;
ASSERT(indp->SRC_length >= 0);
if (indp->SRC_length > ntp->tim_peermaxlen) {
ptr = kmem_alloc(indp->SRC_length,
KM_NOSLEEP);
if (ptr == NULL) {
mutex_exit(&ntp->tim_mutex);
rw_exit(&tim_list_rwlock);
tilog("timodwproc: kmem_alloc "
"failed, attempting "
"recovery\n", 0);
tim_recover(q, mp,
indp->SRC_length);
return (1);
}
if (ntp->tim_peermaxlen > 0)
kmem_free(ntp->tim_peername,
ntp->tim_peermaxlen);
ntp->tim_peername = ptr;
ntp->tim_peermaxlen = indp->SRC_length;
}
ntp->tim_peerlen = indp->SRC_length;
ptr = (caddr_t)indp + indp->SRC_offset;
bcopy(ptr, ntp->tim_peername, ntp->tim_peerlen);
mutex_exit(&ntp->tim_mutex);
cresackout:
rw_exit(&tim_list_rwlock);
tp->tim_flags &=
~(WAIT_CONNRESACK | WAITIOCACK);
freemsg(tp->tim_iocsave);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
}
tim_send_reply(q, mp, tp, pptr->ok_ack.CORRECT_prim);
break;
case T_BIND_ACK: {
struct T_bind_ack *ackp =
(struct T_bind_ack *)mp->b_rptr;
mp->b_datap->db_type = M_PCPROTO;
if (blen < sizeof (*ackp)) {
putnext(q, mp);
break;
}
tp->tim_backlog = ackp->CONIND_number;
if (((tp->tim_flags & WAITIOCACK) == 0) ||
((tp->tim_saved_prim != O_T_BIND_REQ) &&
(tp->tim_saved_prim != T_BIND_REQ))) {
putnext(q, mp);
break;
}
ASSERT(tp->tim_iocsave != NULL);
if (tp->tim_flags & DO_MYNAME) {
caddr_t p;
if (ackp->ADDR_length < 0 ||
mp->b_rptr + ackp->ADDR_offset +
ackp->ADDR_length > mp->b_wptr) {
putnext(q, mp);
break;
}
if (ackp->ADDR_length > tp->tim_mymaxlen) {
p = kmem_alloc(ackp->ADDR_length,
KM_NOSLEEP);
if (p == NULL) {
tilog("timodrproc: kmem_alloc "
"failed attempt recovery",
0);
tim_recover(q, mp,
ackp->ADDR_length);
return (1);
}
ASSERT(tp->tim_mymaxlen >= 0);
if (tp->tim_mymaxlen != 0) {
kmem_free(tp->tim_myname,
tp->tim_mymaxlen);
}
tp->tim_myname = p;
tp->tim_mymaxlen = ackp->ADDR_length;
}
tp->tim_mylen = ackp->ADDR_length;
bcopy(mp->b_rptr + ackp->ADDR_offset,
tp->tim_myname, tp->tim_mylen);
}
tim_ioctl_send_reply(q, tp->tim_iocsave, mp);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
tp->tim_flags &= ~(WAITIOCACK | WAIT_IOCINFOACK |
TI_CAP_RECVD | CAP_WANTS_INFO);
break;
}
case T_OPTMGMT_ACK:
tilog("timodrproc: Got T_OPTMGMT_ACK\n", 0);
mp->b_datap->db_type = M_PCPROTO;
if (((tp->tim_flags & WAITIOCACK) == 0) ||
((tp->tim_saved_prim != T_SVR4_OPTMGMT_REQ) &&
(tp->tim_saved_prim != T_OPTMGMT_REQ))) {
putnext(q, mp);
} else {
ASSERT(tp->tim_iocsave != NULL);
tim_ioctl_send_reply(q, tp->tim_iocsave, mp);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
tp->tim_flags &= ~(WAITIOCACK |
WAIT_IOCINFOACK | TI_CAP_RECVD |
CAP_WANTS_INFO);
}
break;
case T_INFO_ACK: {
struct T_info_ack *tia = (struct T_info_ack *)pptr;
mp->b_datap->db_type = M_PCPROTO;
if (blen < sizeof (*tia)) {
putnext(q, mp);
break;
}
tilog("timodrproc: Got T_INFO_ACK, flags = %x\n",
tp->tim_flags);
timodprocessinfo(q, tp, tia);
TILOG("timodrproc: flags = %x\n", tp->tim_flags);
if ((tp->tim_flags & WAITIOCACK) != 0) {
size_t expected_ack_size;
ssize_t deficit;
int ioc_cmd;
struct T_capability_ack *tcap;
if (!(tp->tim_flags & WAIT_IOCINFOACK)) {
putnext(q, mp);
break;
}
ASSERT(tp->tim_iocsave != NULL);
iocbp = (struct iocblk *)tp->tim_iocsave->b_rptr;
ioc_cmd = iocbp->ioc_cmd;
if (ioc_cmd == TI_CAPABILITY) {
struct T_info_ack saved_info;
if ((tp->tim_flags &
(TI_CAP_RECVD | CAP_WANTS_INFO)) !=
(TI_CAP_RECVD | CAP_WANTS_INFO)) {
putnext(q, mp);
break;
}
TILOG("timodrproc: emulating TI_CAPABILITY/"
"info\n", 0);
saved_info = *tia;
mp = tpi_ack_alloc(mp,
sizeof (struct T_capability_ack),
M_PCPROTO, T_CAPABILITY_ACK);
if (mp == NULL) {
tilog("timodrproc: realloc failed, "
"no recovery attempted\n", 0);
return (1);
}
tcap = (struct T_capability_ack *)mp->b_rptr;
tcap->CAP_bits1 = TC1_INFO;
tcap->INFO_ack = saved_info;
tp->tim_flags &= ~(WAITIOCACK |
WAIT_IOCINFOACK | TI_CAP_RECVD |
CAP_WANTS_INFO);
tim_ioctl_send_reply(q, tp->tim_iocsave, mp);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
break;
}
if (ioc_cmd != TI_GETINFO && ioc_cmd != TI_SYNC) {
putnext(q, mp);
break;
}
expected_ack_size =
sizeof (struct T_info_ack);
if (iocbp->ioc_cmd == TI_SYNC) {
expected_ack_size = 2 * sizeof (uint32_t) +
sizeof (struct ti_sync_ack);
}
deficit = expected_ack_size - blen;
if (deficit != 0) {
if (mp->b_datap->db_lim - mp->b_wptr <
deficit) {
mblk_t *tmp = allocb(expected_ack_size,
BPRI_HI);
if (tmp == NULL) {
ASSERT(MBLKSIZE(mp) >=
sizeof (struct T_error_ack));
tilog("timodrproc: allocb failed no "
"recovery attempt\n", 0);
mp->b_rptr = mp->b_datap->db_base;
pptr = (union T_primitives *)
mp->b_rptr;
pptr->error_ack.ERROR_prim = T_INFO_REQ;
pptr->error_ack.TLI_error = TSYSERR;
pptr->error_ack.UNIX_error = EAGAIN;
pptr->error_ack.PRIM_type = T_ERROR_ACK;
mp->b_datap->db_type = M_PCPROTO;
tim_send_ioc_error_ack(q, tp, mp);
break;
} else {
bcopy(mp->b_rptr, tmp->b_rptr, blen);
tmp->b_wptr += blen;
pptr = (union T_primitives *)
tmp->b_rptr;
freemsg(mp);
mp = tmp;
}
}
}
if (iocbp->ioc_cmd == TI_SYNC) {
struct ti_sync_ack *tsap =
(struct ti_sync_ack *)mp->b_rptr;
tsap->tsa_qlen = tp->tim_backlog;
tsap->tsa_flags = 0x0;
if (tp->tim_flags & PEEK_RDQ_EXPIND) {
if (ti_expind_on_rdqueues(q)) {
tsap->tsa_flags |=
TSAF_EXP_QUEUED;
}
tp->tim_flags &=
~PEEK_RDQ_EXPIND;
}
mp->b_wptr += 2*sizeof (uint32_t);
}
tim_ioctl_send_reply(q, tp->tim_iocsave, mp);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
tp->tim_flags &= ~(WAITIOCACK | WAIT_IOCINFOACK |
TI_CAP_RECVD | CAP_WANTS_INFO);
break;
}
}
putnext(q, mp);
break;
case T_ADDR_ACK:
tilog("timodrproc: Got T_ADDR_ACK\n", 0);
tim_send_reply(q, mp, tp, T_ADDR_REQ);
break;
case T_CONN_IND: {
struct T_conn_ind *tcip =
(struct T_conn_ind *)mp->b_rptr;
tilog("timodrproc: Got T_CONN_IND\n", 0);
if (blen >= sizeof (*tcip) &&
MBLKIN(mp, tcip->SRC_offset, tcip->SRC_length)) {
if (((nbp = dupmsg(mp)) != NULL) ||
((nbp = copymsg(mp)) != NULL)) {
nbp->b_next = tp->tim_consave;
tp->tim_consave = nbp;
} else {
tim_recover(q, mp,
(t_scalar_t)sizeof (mblk_t));
return (1);
}
}
if (auditing)
audit_sock(T_CONN_IND, q, mp, TIMOD_ID);
putnext(q, mp);
break;
}
case T_CONN_CON:
mutex_enter(&tp->tim_mutex);
if (tp->tim_peercred != NULL)
crfree(tp->tim_peercred);
tp->tim_peercred = msg_getcred(mp, &tp->tim_cpid);
if (tp->tim_peercred != NULL)
crhold(tp->tim_peercred);
mutex_exit(&tp->tim_mutex);
tilog("timodrproc: Got T_CONN_CON\n", 0);
tp->tim_flags &= ~CONNWAIT;
putnext(q, mp);
break;
case T_DISCON_IND: {
struct T_discon_ind *disp;
struct T_conn_ind *conp;
mblk_t *pbp = NULL;
if (q->q_first != 0)
tilog("timodrput: T_DISCON_IND - flow control\n", 0);
if (blen < sizeof (*disp)) {
putnext(q, mp);
break;
}
disp = (struct T_discon_ind *)mp->b_rptr;
tilog("timodrproc: Got T_DISCON_IND Reason: %d\n",
disp->DISCON_reason);
tp->tim_flags &= ~(CONNWAIT|LOCORDREL|REMORDREL);
tim_clear_peer(tp);
for (nbp = tp->tim_consave; nbp; nbp = nbp->b_next) {
conp = (struct T_conn_ind *)nbp->b_rptr;
if (conp->SEQ_number == disp->SEQ_number)
break;
pbp = nbp;
}
if (nbp) {
if (pbp)
pbp->b_next = nbp->b_next;
else
tp->tim_consave = nbp->b_next;
nbp->b_next = NULL;
freemsg(nbp);
}
putnext(q, mp);
break;
}
case T_ORDREL_IND:
tilog("timodrproc: Got T_ORDREL_IND\n", 0);
if (tp->tim_flags & LOCORDREL) {
tp->tim_flags &= ~(LOCORDREL|REMORDREL);
tim_clear_peer(tp);
} else {
tp->tim_flags |= REMORDREL;
}
putnext(q, mp);
break;
case T_EXDATA_IND:
case T_DATA_IND:
case T_UNITDATA_IND:
if (pptr->type == T_EXDATA_IND)
tilog("timodrproc: Got T_EXDATA_IND\n", 0);
if (!bcanputnext(q, mp->b_band)) {
(void) putbq(q, mp);
return (1);
}
putnext(q, mp);
break;
case T_CAPABILITY_ACK: {
struct T_capability_ack *tca;
if (blen < sizeof (*tca)) {
putnext(q, mp);
break;
}
tilog("timodrproc: Got T_CAPABILITY_ACK\n", 0);
PI_PROVLOCK(tp->tim_provinfo);
if (tp->tim_provinfo->tpi_capability != PI_YES)
tp->tim_provinfo->tpi_capability = PI_YES;
PI_PROVUNLOCK(tp->tim_provinfo);
if (tp->tim_tcap_timoutid != 0) {
(void) quntimeout(q, tp->tim_tcap_timoutid);
tp->tim_tcap_timoutid = 0;
}
tca = (struct T_capability_ack *)mp->b_rptr;
if (tca->CAP_bits1 & TC1_INFO)
timodprocessinfo(q, tp, &tca->INFO_ack);
tim_send_reply(q, mp, tp, T_CAPABILITY_REQ);
}
break;
}
break;
case M_FLUSH:
tilog("timodrproc: Got M_FLUSH\n", 0);
if (*mp->b_rptr & FLUSHR) {
if (*mp->b_rptr & FLUSHBAND)
flushband(q, *(mp->b_rptr + 1), FLUSHDATA);
else
flushq(q, FLUSHDATA);
}
putnext(q, mp);
break;
case M_IOCACK:
iocbp = (struct iocblk *)mp->b_rptr;
tilog("timodrproc: Got M_IOCACK\n", 0);
if (iocbp->ioc_cmd == TI_GETMYNAME) {
if ((tp->tim_flags & DO_MYNAME) != 0) {
tp->tim_flags &= ~DO_MYNAME;
PI_PROVLOCK(tp->tim_provinfo);
tp->tim_provinfo->tpi_myname = PI_YES;
PI_PROVUNLOCK(tp->tim_provinfo);
}
ASSERT(tp->tim_mymaxlen >= 0);
if (tp->tim_mymaxlen != 0) {
kmem_free(tp->tim_myname, (size_t)tp->tim_mymaxlen);
tp->tim_myname = NULL;
tp->tim_mymaxlen = 0;
}
if (tp->tim_saved_prim == -1) {
freemsg(tp->tim_iocsave);
tp->tim_iocsave = NULL;
}
} else if (iocbp->ioc_cmd == TI_GETPEERNAME) {
boolean_t clearit;
if ((tp->tim_flags & DO_PEERNAME) != 0) {
tp->tim_flags &= ~DO_PEERNAME;
PI_PROVLOCK(tp->tim_provinfo);
tp->tim_provinfo->tpi_peername = PI_YES;
PI_PROVUNLOCK(tp->tim_provinfo);
}
mutex_enter(&tp->tim_mutex);
ASSERT(tp->tim_peermaxlen >= 0);
clearit = tp->tim_peermaxlen != 0;
if (clearit) {
kmem_free(tp->tim_peername, tp->tim_peermaxlen);
tp->tim_peername = NULL;
tp->tim_peermaxlen = 0;
tp->tim_peerlen = 0;
}
mutex_exit(&tp->tim_mutex);
if (clearit) {
mblk_t *bp;
bp = tp->tim_consave;
while (bp != NULL) {
nbp = bp->b_next;
bp->b_next = NULL;
freemsg(bp);
bp = nbp;
}
tp->tim_consave = NULL;
}
if (tp->tim_saved_prim == -1) {
freemsg(tp->tim_iocsave);
tp->tim_iocsave = NULL;
}
}
putnext(q, mp);
break;
case M_IOCNAK:
tilog("timodrproc: Got M_IOCNAK\n", 0);
iocbp = (struct iocblk *)mp->b_rptr;
if (((iocbp->ioc_cmd == TI_GETMYNAME) ||
(iocbp->ioc_cmd == TI_GETPEERNAME)) &&
((iocbp->ioc_error == EINVAL) || (iocbp->ioc_error == 0))) {
PI_PROVLOCK(tp->tim_provinfo);
if (iocbp->ioc_cmd == TI_GETMYNAME) {
if (tp->tim_provinfo->tpi_myname == PI_DONTKNOW)
tp->tim_provinfo->tpi_myname = PI_NO;
} else if (iocbp->ioc_cmd == TI_GETPEERNAME) {
if (tp->tim_provinfo->tpi_peername == PI_DONTKNOW)
tp->tim_provinfo->tpi_peername = PI_NO;
}
PI_PROVUNLOCK(tp->tim_provinfo);
if ((tp->tim_iocsave != NULL) &&
(tp->tim_saved_prim == -1)) {
freemsg(mp);
mp = tp->tim_iocsave;
tp->tim_iocsave = NULL;
tp->tim_flags |= NAMEPROC;
if (ti_doname(WR(q), mp) != DONAME_CONT) {
tp->tim_flags &= ~NAMEPROC;
}
break;
}
}
putnext(q, mp);
break;
}
return (0);
}
static int
timodwput(queue_t *q, mblk_t *mp)
{
union T_primitives *pptr;
struct tim_tim *tp;
struct iocblk *iocbp;
if (q->q_first != 0 && mp->b_datap->db_type < QPCTL) {
if (mp->b_datap->db_type == M_IOCTL) {
iocbp = (struct iocblk *)mp->b_rptr;
switch (iocbp->ioc_cmd) {
default:
(void) putq(q, mp);
return (0);
case TI_GETINFO:
case TI_SYNC:
case TI_CAPABILITY:
break;
}
} else {
(void) putq(q, mp);
return (0);
}
}
switch (mp->b_datap->db_type) {
case M_DATA:
tp = (struct tim_tim *)q->q_ptr;
ASSERT(tp);
if (tp->tim_flags & CLTS) {
mblk_t *tmp;
if ((tmp = tim_filladdr(q, mp, B_FALSE)) == NULL) {
(void) putq(q, mp);
break;
} else {
mp = tmp;
}
}
if (bcanputnext(q, mp->b_band))
putnext(q, mp);
else
(void) putq(q, mp);
break;
case M_PROTO:
case M_PCPROTO:
pptr = (union T_primitives *)mp->b_rptr;
switch (pptr->type) {
case T_UNITDATA_REQ:
tp = (struct tim_tim *)q->q_ptr;
ASSERT(tp);
if (tp->tim_flags & CLTS) {
mblk_t *tmp;
tmp = tim_filladdr(q, mp, B_FALSE);
if (tmp == NULL) {
(void) putq(q, mp);
break;
} else {
mp = tmp;
}
}
if (bcanputnext(q, mp->b_band))
putnext(q, mp);
else
(void) putq(q, mp);
break;
case T_DATA_REQ:
case T_EXDATA_REQ:
if (bcanputnext(q, mp->b_band))
putnext(q, mp);
else
(void) putq(q, mp);
break;
default:
(void) timodwproc(q, mp);
break;
}
break;
default:
(void) timodwproc(q, mp);
break;
}
return (0);
}
static int
timodwsrv(queue_t *q)
{
mblk_t *mp;
ASSERT(q != NULL);
if (q->q_ptr == NULL)
return (0);
while ((mp = getq(q)) != NULL) {
if (timodwproc(q, mp)) {
return (0);
}
}
return (0);
}
static int
timodwproc(queue_t *q, mblk_t *mp)
{
union T_primitives *pptr;
struct tim_tim *tp;
uint32_t auditing = AU_AUDITING();
mblk_t *tmp;
struct iocblk *iocbp;
int error;
tp = (struct tim_tim *)q->q_ptr;
switch (mp->b_datap->db_type) {
default:
putnext(q, mp);
break;
case M_DATA:
if (tp->tim_flags & CLTS) {
if ((tmp = tim_filladdr(q, mp, B_TRUE)) == NULL) {
return (1);
} else {
mp = tmp;
}
}
if (!bcanputnext(q, mp->b_band)) {
(void) putbq(q, mp);
return (1);
}
putnext(q, mp);
break;
case M_IOCTL:
iocbp = (struct iocblk *)mp->b_rptr;
TILOG("timodwproc: Got M_IOCTL(%d)\n", iocbp->ioc_cmd);
ASSERT(MBLKL(mp) == sizeof (struct iocblk));
if (tp->tim_flags & WAITIOCACK) {
TILOG("timodwproc: putbq M_IOCTL(%d)\n",
iocbp->ioc_cmd);
(void) putbq(q, mp);
if (!(q->q_flag & QWANTR))
tim_ioctl_retry(q);
return (1);
}
switch (iocbp->ioc_cmd) {
default:
putnext(q, mp);
break;
case _I_GETPEERCRED:
if ((tp->tim_flags & COTS) == 0) {
miocnak(q, mp, 0, ENOTSUP);
} else {
mblk_t *cmp = mp->b_cont;
k_peercred_t *kp = NULL;
mutex_enter(&tp->tim_mutex);
if (cmp != NULL &&
iocbp->ioc_flag == IOC_NATIVE &&
(tp->tim_flags &
(CONNWAIT|LOCORDREL|REMORDREL)) == 0 &&
tp->tim_peercred != NULL &&
DB_TYPE(cmp) == M_DATA &&
MBLKL(cmp) == sizeof (k_peercred_t)) {
kp = (k_peercred_t *)cmp->b_rptr;
crhold(kp->pc_cr = tp->tim_peercred);
kp->pc_cpid = tp->tim_cpid;
}
mutex_exit(&tp->tim_mutex);
if (kp != NULL)
miocack(q, mp, sizeof (*kp), 0);
else
miocnak(q, mp, 0, ENOTCONN);
}
break;
case TI_BIND:
case TI_UNBIND:
case TI_OPTMGMT:
case TI_GETADDRS:
TILOG("timodwproc: TI_{BIND|UNBIND|OPTMGMT|GETADDRS}"
"\n", 0);
error = miocpullup(mp, sizeof (t_scalar_t));
if (error != 0) {
miocnak(q, mp, 0, error);
break;
}
tim_send_ioctl_tpi_msg(q, mp, tp, iocbp);
break;
case TI_GETINFO:
TILOG("timodwproc: TI_GETINFO\n", 0);
error = miocpullup(mp, sizeof (struct T_info_req));
if (error != 0) {
miocnak(q, mp, 0, error);
break;
}
tp->tim_flags |= WAIT_IOCINFOACK;
tim_send_ioctl_tpi_msg(q, mp, tp, iocbp);
break;
case TI_SYNC: {
mblk_t *tsr_mp;
struct ti_sync_req *tsr;
uint32_t tsr_flags;
error = miocpullup(mp, sizeof (struct ti_sync_req));
if (error != 0) {
miocnak(q, mp, 0, error);
break;
}
tsr_mp = mp->b_cont;
tsr = (struct ti_sync_req *)tsr_mp->b_rptr;
TILOG("timodwproc: TI_SYNC(%x)\n", tsr->tsr_flags);
tsr_flags = tsr->tsr_flags;
if ((tsr_flags & TSRF_INFO_REQ) == 0) {
mblk_t *ack_mp = reallocb(tsr_mp,
sizeof (struct ti_sync_ack), 0);
mp->b_cont = NULL;
if (ack_mp == NULL) {
tilog("timodwproc: allocb failed no "
"recovery attempt\n", 0);
freemsg(tsr_mp);
miocnak(q, mp, 0, ENOMEM);
} else {
tim_answer_ti_sync(q, mp, tp,
ack_mp, tsr_flags);
}
break;
}
if (tsr_flags & TSRF_IS_EXP_IN_RCVBUF)
tp->tim_flags |= PEEK_RDQ_EXPIND;
ASSERT(MBLKL(tsr_mp) >= sizeof (struct T_info_req));
((struct T_info_req *)tsr_mp->b_rptr)->PRIM_type =
T_INFO_REQ;
tsr_mp->b_wptr = tsr_mp->b_rptr +
sizeof (struct T_info_req);
tp->tim_flags |= WAIT_IOCINFOACK;
tim_send_ioctl_tpi_msg(q, mp, tp, iocbp);
}
break;
case TI_CAPABILITY: {
mblk_t *tcsr_mp;
struct T_capability_req *tcr;
error = miocpullup(mp, sizeof (*tcr));
if (error != 0) {
miocnak(q, mp, 0, error);
break;
}
tcsr_mp = mp->b_cont;
tcr = (struct T_capability_req *)tcsr_mp->b_rptr;
TILOG("timodwproc: TI_CAPABILITY(CAP_bits1 = %x)\n",
tcr->CAP_bits1);
if (tcr->PRIM_type != T_CAPABILITY_REQ) {
TILOG("timodwproc: invalid msg type %d\n",
tcr->PRIM_type);
miocnak(q, mp, 0, EPROTO);
break;
}
switch (tp->tim_provinfo->tpi_capability) {
case PI_YES:
tim_send_ioctl_tpi_msg(q, mp, tp, iocbp);
break;
case PI_DONTKNOW:
ASSERT(tp->tim_tcap_timoutid == 0);
if ((tcr->CAP_bits1 & TC1_INFO) == 0) {
tp->tim_flags |= TI_CAP_RECVD;
} else {
tp->tim_flags |= (TI_CAP_RECVD |
CAP_WANTS_INFO);
}
tp->tim_tcap_timoutid = qtimeout(q,
tim_tcap_timer, q, tim_tcap_wait * hz);
tim_send_ioctl_tpi_msg(q, mp, tp, iocbp);
break;
case PI_NO:
if ((tcr->CAP_bits1 & TC1_INFO) != 0) {
tp->tim_flags |= (TI_CAP_RECVD |
CAP_WANTS_INFO | WAIT_IOCINFOACK);
TILOG("timodwproc: sending down "
"T_INFO_REQ, flags = %x\n",
tp->tim_flags);
((struct T_info_req *)tcsr_mp->b_rptr)->
PRIM_type = T_INFO_REQ;
tcsr_mp->b_wptr = tcsr_mp->b_rptr +
sizeof (struct T_info_req);
tim_send_ioctl_tpi_msg(q, mp, tp,
iocbp);
break;
}
mp->b_cont = tcsr_mp = tpi_ack_alloc(mp->b_cont,
sizeof (struct T_capability_ack), M_PCPROTO,
T_CAPABILITY_ACK);
if (tcsr_mp == NULL) {
tilog("timodwproc: allocb failed no "
"recovery attempt\n", 0);
miocnak(q, mp, 0, ENOMEM);
break;
}
tp->tim_flags &= ~(WAITIOCACK | TI_CAP_RECVD |
WAIT_IOCINFOACK | CAP_WANTS_INFO);
((struct T_capability_ack *)
tcsr_mp->b_rptr)->CAP_bits1 = 0;
tim_ioctl_send_reply(q, mp, tcsr_mp);
if (tp->tim_iocsave != NULL) {
freemsg(tp->tim_iocsave);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
}
break;
default:
cmn_err(CE_PANIC,
"timodwproc: unknown tpi_capability value "
"%d\n", tp->tim_provinfo->tpi_capability);
break;
}
}
break;
case TI_GETMYNAME:
tilog("timodwproc: Got TI_GETMYNAME\n", 0);
if (tp->tim_provinfo->tpi_myname == PI_YES) {
putnext(q, mp);
break;
}
goto getname;
case TI_GETPEERNAME:
tilog("timodwproc: Got TI_GETPEERNAME\n", 0);
if (tp->tim_provinfo->tpi_peername == PI_YES) {
putnext(q, mp);
break;
}
getname:
if ((tmp = copymsg(mp)) == NULL) {
tim_recover(q, mp, msgsize(mp));
return (1);
}
freemsg(tp->tim_iocsave);
tp->tim_iocsave = mp;
tp->tim_saved_prim = -1;
putnext(q, tmp);
break;
}
break;
case M_IOCDATA:
if (tp->tim_flags & NAMEPROC) {
if (ti_doname(q, mp) != DONAME_CONT) {
tp->tim_flags &= ~NAMEPROC;
}
} else
putnext(q, mp);
break;
case M_PROTO:
case M_PCPROTO:
if (MBLKL(mp) < sizeof (t_scalar_t)) {
merror(q, mp, EPROTO);
return (1);
}
pptr = (union T_primitives *)mp->b_rptr;
switch (pptr->type) {
default:
putnext(q, mp);
break;
case T_EXDATA_REQ:
case T_DATA_REQ:
if (pptr->type == T_EXDATA_REQ)
tilog("timodwproc: Got T_EXDATA_REQ\n", 0);
if (!bcanputnext(q, mp->b_band)) {
(void) putbq(q, mp);
return (1);
}
putnext(q, mp);
break;
case T_UNITDATA_REQ:
if (tp->tim_flags & CLTS) {
tmp = tim_filladdr(q, mp, B_TRUE);
if (tmp == NULL) {
return (1);
} else {
mp = tmp;
}
}
if (auditing)
audit_sock(T_UNITDATA_REQ, q, mp, TIMOD_ID);
if (!bcanputnext(q, mp->b_band)) {
(void) putbq(q, mp);
return (1);
}
putnext(q, mp);
break;
case T_CONN_REQ: {
struct T_conn_req *reqp = (struct T_conn_req *)
mp->b_rptr;
void *p;
tilog("timodwproc: Got T_CONN_REQ\n", 0);
if (MBLKL(mp) < sizeof (struct T_conn_req)) {
merror(q, mp, EPROTO);
return (1);
}
if (tp->tim_flags & DO_PEERNAME) {
if (!MBLKIN(mp, reqp->DEST_offset,
reqp->DEST_length)) {
merror(q, mp, EPROTO);
return (1);
}
ASSERT(reqp->DEST_length >= 0);
mutex_enter(&tp->tim_mutex);
if (reqp->DEST_length > tp->tim_peermaxlen) {
p = kmem_alloc(reqp->DEST_length,
KM_NOSLEEP);
if (p == NULL) {
mutex_exit(&tp->tim_mutex);
tilog("timodwproc: kmem_alloc "
"failed, attempting "
"recovery\n", 0);
tim_recover(q, mp,
reqp->DEST_length);
return (1);
}
if (tp->tim_peermaxlen)
kmem_free(tp->tim_peername,
tp->tim_peermaxlen);
tp->tim_peername = p;
tp->tim_peermaxlen = reqp->DEST_length;
}
tp->tim_peerlen = reqp->DEST_length;
p = mp->b_rptr + reqp->DEST_offset;
bcopy(p, tp->tim_peername, tp->tim_peerlen);
mutex_exit(&tp->tim_mutex);
}
if (tp->tim_flags & COTS)
tp->tim_flags |= CONNWAIT;
if (auditing)
audit_sock(T_CONN_REQ, q, mp, TIMOD_ID);
putnext(q, mp);
break;
}
case O_T_CONN_RES:
case T_CONN_RES: {
struct T_conn_res *resp;
struct T_conn_ind *indp;
mblk_t *pmp = NULL;
mblk_t *nbp;
if (MBLKL(mp) < sizeof (struct T_conn_res) ||
(tp->tim_flags & WAITIOCACK)) {
merror(q, mp, EPROTO);
return (1);
}
resp = (struct T_conn_res *)mp->b_rptr;
for (tmp = tp->tim_consave; tmp != NULL;
tmp = tmp->b_next) {
indp = (struct T_conn_ind *)tmp->b_rptr;
if (indp->SEQ_number == resp->SEQ_number)
break;
pmp = tmp;
}
if (tmp == NULL)
goto cresout;
if ((nbp = dupb(mp)) == NULL &&
(nbp = copyb(mp)) == NULL) {
tim_recover(q, mp, msgsize(mp));
return (1);
}
if (pmp != NULL)
pmp->b_next = tmp->b_next;
else
tp->tim_consave = tmp->b_next;
tmp->b_next = NULL;
nbp->b_cont = tmp;
freemsg(tp->tim_iocsave);
tp->tim_iocsave = nbp;
tp->tim_saved_prim = pptr->type;
tp->tim_flags |= WAIT_CONNRESACK | WAITIOCACK;
cresout:
putnext(q, mp);
break;
}
case T_DISCON_REQ: {
struct T_discon_req *disp;
struct T_conn_ind *conp;
mblk_t *pmp = NULL;
if (MBLKL(mp) < sizeof (struct T_discon_req)) {
merror(q, mp, EPROTO);
return (1);
}
disp = (struct T_discon_req *)mp->b_rptr;
tp->tim_flags &= ~(CONNWAIT|LOCORDREL|REMORDREL);
tim_clear_peer(tp);
for (tmp = tp->tim_consave; tmp; tmp = tmp->b_next) {
conp = (struct T_conn_ind *)tmp->b_rptr;
if (conp->SEQ_number == disp->SEQ_number)
break;
pmp = tmp;
}
if (tmp) {
if (pmp)
pmp->b_next = tmp->b_next;
else
tp->tim_consave = tmp->b_next;
tmp->b_next = NULL;
freemsg(tmp);
}
putnext(q, mp);
break;
}
case T_ORDREL_REQ:
if (tp->tim_flags & REMORDREL) {
tp->tim_flags &= ~(LOCORDREL|REMORDREL);
tim_clear_peer(tp);
} else {
tp->tim_flags |= LOCORDREL;
}
putnext(q, mp);
break;
case T_CAPABILITY_REQ:
tilog("timodwproc: Got T_CAPABILITY_REQ\n", 0);
putnext(q, mp);
break;
}
break;
case M_FLUSH:
tilog("timodwproc: Got M_FLUSH\n", 0);
if (*mp->b_rptr & FLUSHW) {
if (*mp->b_rptr & FLUSHBAND)
flushband(q, *(mp->b_rptr + 1), FLUSHDATA);
else
flushq(q, FLUSHDATA);
}
putnext(q, mp);
break;
}
return (0);
}
static void
tilog(char *str, t_scalar_t arg)
{
if (dotilog) {
if (dotilog & 2)
cmn_err(CE_CONT, str, arg);
if (dotilog & 4)
(void) strlog(TIMOD_ID, -1, 0, SL_TRACE | SL_ERROR,
str, arg);
else
(void) strlog(TIMOD_ID, -1, 0, SL_TRACE, str, arg);
}
}
static void
tilogp(char *str, uintptr_t arg)
{
if (dotilog) {
if (dotilog & 2)
cmn_err(CE_CONT, str, arg);
if (dotilog & 4)
(void) strlog(TIMOD_ID, -1, 0, SL_TRACE | SL_ERROR,
str, arg);
else
(void) strlog(TIMOD_ID, -1, 0, SL_TRACE, str, arg);
}
}
static int
ti_doname(
queue_t *q,
mblk_t *mp)
{
struct iocblk *iocp;
struct copyreq *cqp;
STRUCT_HANDLE(strbuf, sb);
struct copyresp *csp;
int ret;
mblk_t *bp;
struct tim_tim *tp = q->q_ptr;
boolean_t getpeer;
switch (mp->b_datap->db_type) {
case M_IOCTL:
iocp = (struct iocblk *)mp->b_rptr;
if ((iocp->ioc_cmd != TI_GETMYNAME) &&
(iocp->ioc_cmd != TI_GETPEERNAME)) {
tilog("ti_doname: bad M_IOCTL command\n", 0);
miocnak(q, mp, 0, EINVAL);
ret = DONAME_FAIL;
break;
}
if ((iocp->ioc_count != TRANSPARENT)) {
miocnak(q, mp, 0, EINVAL);
ret = DONAME_FAIL;
break;
}
cqp = (struct copyreq *)mp->b_rptr;
cqp->cq_private = mp->b_cont;
cqp->cq_addr = (caddr_t)*(intptr_t *)mp->b_cont->b_rptr;
mp->b_cont = NULL;
cqp->cq_size = SIZEOF_STRUCT(strbuf, iocp->ioc_flag);
cqp->cq_flag = 0;
mp->b_datap->db_type = M_COPYIN;
mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
qreply(q, mp);
ret = DONAME_CONT;
break;
case M_IOCDATA:
csp = (struct copyresp *)mp->b_rptr;
iocp = (struct iocblk *)mp->b_rptr;
cqp = (struct copyreq *)mp->b_rptr;
if ((csp->cp_cmd != TI_GETMYNAME) &&
(csp->cp_cmd != TI_GETPEERNAME)) {
cmn_err(CE_WARN, "ti_doname: bad M_IOCDATA command\n");
miocnak(q, mp, 0, EINVAL);
ret = DONAME_FAIL;
break;
}
if (csp->cp_rval) {
freemsg(csp->cp_private);
freemsg(mp);
ret = DONAME_FAIL;
break;
}
ASSERT(csp->cp_private != NULL);
getpeer = csp->cp_cmd == TI_GETPEERNAME;
if (getpeer)
mutex_enter(&tp->tim_mutex);
if (csp->cp_private->b_cont == NULL) {
ASSERT(mp->b_cont);
STRUCT_SET_HANDLE(sb, iocp->ioc_flag,
(void *)mp->b_cont->b_rptr);
if (getpeer) {
if (tp->tim_peerlen == 0) {
STRUCT_FSET(sb, len, 0);
} else if (tp->tim_peerlen >
STRUCT_FGET(sb, maxlen)) {
mutex_exit(&tp->tim_mutex);
miocnak(q, mp, 0, ENAMETOOLONG);
ret = DONAME_FAIL;
break;
} else {
STRUCT_FSET(sb, len, tp->tim_peerlen);
}
} else {
if (tp->tim_mylen == 0) {
STRUCT_FSET(sb, len, 0);
} else if (tp->tim_mylen >
STRUCT_FGET(sb, maxlen)) {
freemsg(csp->cp_private);
miocnak(q, mp, 0, ENAMETOOLONG);
ret = DONAME_FAIL;
break;
} else {
STRUCT_FSET(sb, len, tp->tim_mylen);
}
}
csp->cp_private->b_cont = mp->b_cont;
mp->b_cont = NULL;
}
STRUCT_SET_HANDLE(sb, iocp->ioc_flag,
(void *)csp->cp_private->b_cont->b_rptr);
if (STRUCT_FGET(sb, len) == 0) {
if (getpeer)
STRUCT_FSET(sb, len, tp->tim_peerlen);
else
STRUCT_FSET(sb, len, tp->tim_mylen);
if (getpeer)
mutex_exit(&tp->tim_mutex);
if (STRUCT_FGET(sb, maxlen) == 0) {
freemsg(csp->cp_private);
tim_ioctl_send_reply(q, mp, NULL);
ret = DONAME_DONE;
break;
}
if ((bp = allocb(STRUCT_SIZE(sb), BPRI_MED)) == NULL) {
tilog(
"ti_doname: allocb failed no recovery attempt\n", 0);
freemsg(csp->cp_private);
miocnak(q, mp, 0, EAGAIN);
ret = DONAME_FAIL;
break;
}
bp->b_wptr += STRUCT_SIZE(sb);
bcopy(STRUCT_BUF(sb), bp->b_rptr, STRUCT_SIZE(sb));
cqp->cq_addr =
(caddr_t)*(intptr_t *)csp->cp_private->b_rptr;
cqp->cq_size = STRUCT_SIZE(sb);
cqp->cq_flag = 0;
mp->b_datap->db_type = M_COPYOUT;
mp->b_cont = bp;
STRUCT_FSET(sb, len, 0);
STRUCT_FSET(sb, maxlen, 0);
qreply(q, mp);
ret = DONAME_CONT;
break;
}
if ((bp = allocb((size_t)STRUCT_FGET(sb, len), BPRI_MED))
== NULL) {
if (getpeer)
mutex_exit(&tp->tim_mutex);
tilog("ti_doname: allocb failed no recovery attempt\n",
0);
freemsg(csp->cp_private);
miocnak(q, mp, 0, EAGAIN);
ret = DONAME_FAIL;
break;
}
bp->b_wptr += STRUCT_FGET(sb, len);
if (getpeer) {
bcopy(tp->tim_peername, bp->b_rptr,
STRUCT_FGET(sb, len));
mutex_exit(&tp->tim_mutex);
} else {
bcopy(tp->tim_myname, bp->b_rptr, STRUCT_FGET(sb, len));
}
cqp->cq_addr = (caddr_t)STRUCT_FGETP(sb, buf);
cqp->cq_size = STRUCT_FGET(sb, len);
cqp->cq_flag = 0;
mp->b_datap->db_type = M_COPYOUT;
mp->b_cont = bp;
STRUCT_FSET(sb, len, 0);
qreply(q, mp);
ret = DONAME_CONT;
break;
default:
tilog("ti_doname: freeing bad message type = %d\n",
mp->b_datap->db_type);
freemsg(mp);
ret = DONAME_FAIL;
break;
}
return (ret);
}
static mblk_t *
tim_filladdr(queue_t *q, mblk_t *mp, boolean_t dorecover)
{
mblk_t *bp;
struct tim_tim *tp;
struct T_unitdata_req *up;
struct T_unitdata_req *nup;
size_t plen;
tp = (struct tim_tim *)q->q_ptr;
if (mp->b_datap->db_type == M_DATA) {
mutex_enter(&tp->tim_mutex);
bp = allocb(sizeof (struct T_unitdata_req) + tp->tim_peerlen,
BPRI_MED);
if (bp != NULL) {
bp->b_datap->db_type = M_PROTO;
up = (struct T_unitdata_req *)bp->b_rptr;
up->PRIM_type = T_UNITDATA_REQ;
up->DEST_length = tp->tim_peerlen;
bp->b_wptr += sizeof (struct T_unitdata_req);
up->DEST_offset = sizeof (struct T_unitdata_req);
up->OPT_length = 0;
up->OPT_offset = 0;
if (tp->tim_peerlen > 0) {
bcopy(tp->tim_peername, bp->b_wptr,
tp->tim_peerlen);
bp->b_wptr += tp->tim_peerlen;
}
bp->b_cont = mp;
}
} else {
ASSERT(mp->b_datap->db_type == M_PROTO);
up = (struct T_unitdata_req *)mp->b_rptr;
ASSERT(up->PRIM_type == T_UNITDATA_REQ);
if (up->DEST_length != 0)
return (mp);
mutex_enter(&tp->tim_mutex);
bp = allocb(sizeof (struct T_unitdata_req) + up->OPT_length +
tp->tim_peerlen, BPRI_MED);
if (bp != NULL) {
bp->b_datap->db_type = M_PROTO;
nup = (struct T_unitdata_req *)bp->b_rptr;
nup->PRIM_type = T_UNITDATA_REQ;
nup->DEST_length = plen = tp->tim_peerlen;
bp->b_wptr += sizeof (struct T_unitdata_req);
nup->DEST_offset = sizeof (struct T_unitdata_req);
if (plen > 0) {
bcopy(tp->tim_peername, bp->b_wptr, plen);
bp->b_wptr += plen;
}
mutex_exit(&tp->tim_mutex);
if (up->OPT_length == 0) {
nup->OPT_length = 0;
nup->OPT_offset = 0;
} else {
nup->OPT_length = up->OPT_length;
nup->OPT_offset =
sizeof (struct T_unitdata_req) + plen;
bcopy((mp->b_wptr + up->OPT_offset), bp->b_wptr,
up->OPT_length);
bp->b_wptr += up->OPT_length;
}
bp->b_cont = mp->b_cont;
mp->b_cont = NULL;
freeb(mp);
return (bp);
}
}
ASSERT(MUTEX_HELD(&tp->tim_mutex));
if (bp == NULL && dorecover) {
tim_recover(q, mp,
sizeof (struct T_unitdata_req) + tp->tim_peerlen);
}
mutex_exit(&tp->tim_mutex);
return (bp);
}
static void
tim_addlink(struct tim_tim *tp)
{
struct tim_tim **tpp;
struct tim_tim *next;
tpp = &tim_hash[TIM_HASH(tp->tim_acceptor)];
rw_enter(&tim_list_rwlock, RW_WRITER);
if ((next = *tpp) != NULL)
next->tim_ptpn = &tp->tim_next;
tp->tim_next = next;
tp->tim_ptpn = tpp;
*tpp = tp;
tim_cnt++;
rw_exit(&tim_list_rwlock);
}
static void
tim_dellink(struct tim_tim *tp)
{
struct tim_tim *next;
rw_enter(&tim_list_rwlock, RW_WRITER);
if ((next = tp->tim_next) != NULL)
next->tim_ptpn = tp->tim_ptpn;
*(tp->tim_ptpn) = next;
tim_cnt--;
rw_exit(&tim_list_rwlock);
}
static struct tim_tim *
tim_findlink(t_uscalar_t id)
{
struct tim_tim *tp;
ASSERT(rw_lock_held(&tim_list_rwlock));
for (tp = tim_hash[TIM_HASH(id)]; tp != NULL; tp = tp->tim_next) {
if (tp->tim_acceptor == id) {
break;
}
}
return (tp);
}
static void
tim_recover(queue_t *q, mblk_t *mp, t_scalar_t size)
{
struct tim_tim *tp;
bufcall_id_t bid;
timeout_id_t tid;
tp = (struct tim_tim *)q->q_ptr;
if (mp->b_datap->db_type == M_PCPROTO)
mp->b_datap->db_type = M_PROTO;
noenable(q);
(void) putbq(q, mp);
if (q->q_flag & QREADR) {
if (tp->tim_rtimoutid || tp->tim_rbufcid)
return;
} else {
if (tp->tim_wtimoutid || tp->tim_wbufcid)
return;
}
if (!(bid = qbufcall(RD(q), (size_t)size, BPRI_MED, tim_buffer, q))) {
tid = qtimeout(RD(q), tim_timer, q, TIMWAIT);
if (q->q_flag & QREADR)
tp->tim_rtimoutid = tid;
else
tp->tim_wtimoutid = tid;
} else {
if (q->q_flag & QREADR)
tp->tim_rbufcid = bid;
else
tp->tim_wbufcid = bid;
}
}
static void
tim_ioctl_retry(queue_t *q)
{
struct tim_tim *tp;
tp = (struct tim_tim *)q->q_ptr;
if (tp->tim_wtimoutid || tp->tim_wbufcid)
return;
tp->tim_wtimoutid = qtimeout(RD(q), tim_timer, q, TIMIOCWAIT);
}
static int
ti_expind_on_rdqueues(queue_t *rq)
{
mblk_t *bp;
queue_t *q;
q = rq;
claimstr(q);
do {
mutex_enter(QLOCK(rq));
bp = rq->q_first;
while (bp != NULL) {
if ((bp->b_datap->db_type == M_PROTO) &&
((bp->b_wptr - bp->b_rptr) >=
sizeof (struct T_exdata_ind)) &&
(((struct T_exdata_ind *)bp->b_rptr)->PRIM_type
== T_EXDATA_IND)) {
mutex_exit(QLOCK(rq));
releasestr(q);
return (1);
}
bp = bp->b_next;
}
mutex_exit(QLOCK(rq));
rq = rq->q_next;
} while (rq != NULL);
releasestr(q);
return (0);
}
static void
tim_tcap_timer(void *q_ptr)
{
queue_t *q = (queue_t *)q_ptr;
struct tim_tim *tp = (struct tim_tim *)q->q_ptr;
ASSERT(tp != NULL && tp->tim_tcap_timoutid != 0);
ASSERT((tp->tim_flags & TI_CAP_RECVD) != 0);
tp->tim_tcap_timoutid = 0;
TILOG("tim_tcap_timer: fired\n", 0);
tim_tcap_genreply(q, tp);
}
static void
tim_tcap_genreply(queue_t *q, struct tim_tim *tp)
{
mblk_t *mp = tp->tim_iocsave;
struct iocblk *iocbp;
TILOG("timodrproc: tim_tcap_genreply\n", 0);
ASSERT(tp == (struct tim_tim *)q->q_ptr);
ASSERT(mp != NULL);
iocbp = (struct iocblk *)mp->b_rptr;
ASSERT(iocbp != NULL);
ASSERT(MBLKL(mp) == sizeof (struct iocblk));
ASSERT(iocbp->ioc_cmd == TI_CAPABILITY);
ASSERT(mp->b_cont == NULL);
PI_PROVLOCK(tp->tim_provinfo);
if (tp->tim_provinfo->tpi_capability == PI_DONTKNOW)
tp->tim_provinfo->tpi_capability = PI_NO;
PI_PROVUNLOCK(tp->tim_provinfo);
if (tp->tim_tcap_timoutid != 0) {
(void) quntimeout(q, tp->tim_tcap_timoutid);
tp->tim_tcap_timoutid = 0;
}
if ((tp->tim_flags & CAP_WANTS_INFO) != 0) {
mblk_t *tirmp = tpi_ack_alloc(NULL,
sizeof (struct T_info_req), M_PCPROTO, T_INFO_REQ);
if (tirmp != NULL) {
TILOG("emulate_tcap_ioc_req: sending T_INFO_REQ\n", 0);
tp->tim_flags |= WAIT_IOCINFOACK;
putnext(WR(q), tirmp);
} else {
tilog("emulate_tcap_req: allocb fail, "
"no recovery attmpt\n", 0);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
tp->tim_flags &= ~(TI_CAP_RECVD | WAITIOCACK |
CAP_WANTS_INFO | WAIT_IOCINFOACK);
miocnak(q, mp, 0, ENOMEM);
}
} else {
mblk_t *ackmp = tpi_ack_alloc(NULL,
sizeof (struct T_capability_ack), M_PCPROTO,
T_CAPABILITY_ACK);
mp->b_cont = ackmp;
if (ackmp != NULL) {
((struct T_capability_ack *)
ackmp->b_rptr)->CAP_bits1 = 0;
tim_ioctl_send_reply(q, mp, ackmp);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
tp->tim_flags &= ~(WAITIOCACK | WAIT_IOCINFOACK |
TI_CAP_RECVD | CAP_WANTS_INFO);
} else {
tilog("timodwproc:allocb failed no "
"recovery attempt\n", 0);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
tp->tim_flags &= ~(TI_CAP_RECVD | WAITIOCACK |
CAP_WANTS_INFO | WAIT_IOCINFOACK);
miocnak(q, mp, 0, ENOMEM);
}
}
}
static void
tim_ioctl_send_reply(queue_t *q, mblk_t *ioc_mp, mblk_t *mp)
{
struct iocblk *iocbp;
ASSERT(q != NULL && ioc_mp != NULL);
ioc_mp->b_datap->db_type = M_IOCACK;
if (mp != NULL)
mp->b_datap->db_type = M_DATA;
if (ioc_mp->b_cont != mp) {
freemsg(ioc_mp->b_cont);
ioc_mp->b_cont = mp;
}
iocbp = (struct iocblk *)ioc_mp->b_rptr;
iocbp->ioc_error = 0;
iocbp->ioc_rval = 0;
if (mp == NULL)
iocbp->ioc_count = 0;
else if (iocbp->ioc_cmd != TI_CAPABILITY)
iocbp->ioc_count = msgsize(mp);
else {
iocbp->ioc_count = MIN(MBLKL(mp), iocbp->ioc_count);
mp->b_wptr = mp->b_rptr + iocbp->ioc_count;
}
TILOG("iosendreply: ioc_cmd = %d, ", iocbp->ioc_cmd);
putnext(RD(q), ioc_mp);
}
static void
tim_send_ioc_error_ack(queue_t *q, struct tim_tim *tp, mblk_t *mp)
{
struct T_error_ack *tea = (struct T_error_ack *)mp->b_rptr;
t_scalar_t error_prim;
mp->b_wptr = mp->b_rptr + sizeof (struct T_error_ack);
ASSERT(mp->b_wptr <= mp->b_datap->db_lim);
error_prim = tea->ERROR_prim;
ASSERT(tp->tim_iocsave != NULL);
ASSERT(tp->tim_iocsave->b_cont != mp);
q = RD(q);
TILOG("tim_send_ioc_error_ack: prim = %d\n", tp->tim_saved_prim);
if (tp->tim_saved_prim != error_prim) {
putnext(q, mp);
} else if (error_prim == T_CAPABILITY_REQ) {
TILOG("timodrproc: T_ERROR_ACK/T_CAPABILITY_REQ\n", 0);
ASSERT(tp->tim_iocsave->b_cont == NULL);
tim_tcap_genreply(q, tp);
freemsg(mp);
} else {
struct iocblk *iocbp = (struct iocblk *)tp->tim_iocsave->b_rptr;
TILOG("tim_send_ioc_error_ack: T_ERROR_ACK: prim %d\n",
error_prim);
ASSERT(tp->tim_iocsave->b_cont == NULL);
switch (error_prim) {
default:
TILOG("timodrproc: Unknown T_ERROR_ACK: tlierror %d\n",
tea->TLI_error);
putnext(q, mp);
break;
case T_INFO_REQ:
case T_SVR4_OPTMGMT_REQ:
case T_OPTMGMT_REQ:
case O_T_BIND_REQ:
case T_BIND_REQ:
case T_UNBIND_REQ:
case T_ADDR_REQ:
case T_CAPABILITY_REQ:
TILOG("ioc_err_ack: T_ERROR_ACK: tlierror %x\n",
tea->TLI_error);
iocbp->ioc_count = 0;
iocbp->ioc_error = 0;
iocbp->ioc_rval = tea->TLI_error;
if (iocbp->ioc_rval == TSYSERR)
iocbp->ioc_rval |= tea->UNIX_error << 8;
tp->tim_iocsave->b_datap->db_type = M_IOCACK;
freemsg(mp);
putnext(q, tp->tim_iocsave);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
tp->tim_flags &= ~(WAITIOCACK | TI_CAP_RECVD |
CAP_WANTS_INFO | WAIT_IOCINFOACK);
break;
}
}
}
static void
tim_send_reply(queue_t *q, mblk_t *mp, struct tim_tim *tp, t_scalar_t prim)
{
ASSERT(mp != NULL && q != NULL && tp != NULL);
ASSERT(q == RD(q));
mp->b_datap->db_type = M_PCPROTO;
if (((tp->tim_flags & WAITIOCACK) == 0) || (tp->tim_saved_prim != prim))
putnext(q, mp);
else {
ASSERT(tp->tim_iocsave != NULL);
tim_ioctl_send_reply(q, tp->tim_iocsave, mp);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
tp->tim_flags &= ~(WAITIOCACK | WAIT_IOCINFOACK |
TI_CAP_RECVD | CAP_WANTS_INFO);
}
}
static void
tim_answer_ti_sync(queue_t *q, mblk_t *mp, struct tim_tim *tp,
mblk_t *ackmp, uint32_t tsr_flags)
{
struct ti_sync_ack *tsap;
ASSERT(q != NULL && q == WR(q) && ackmp != NULL);
tsap = (struct ti_sync_ack *)ackmp->b_rptr;
bzero(tsap, sizeof (struct ti_sync_ack));
ackmp->b_wptr = ackmp->b_rptr + sizeof (struct ti_sync_ack);
if (tsr_flags == 0 ||
(tsr_flags & ~(TSRF_QLEN_REQ | TSRF_IS_EXP_IN_RCVBUF)) != 0) {
TILOG("timodwproc: unsupported/bad flag setting %x\n",
tsr_flags);
freemsg(ackmp);
miocnak(q, mp, 0, EINVAL);
return;
}
if ((tsr_flags & TSRF_QLEN_REQ) != 0)
tsap->tsa_qlen = tp->tim_backlog;
if ((tsr_flags & TSRF_IS_EXP_IN_RCVBUF) != 0 &&
ti_expind_on_rdqueues(RD(q))) {
tsap->tsa_flags |= TSAF_EXP_QUEUED;
}
tim_ioctl_send_reply(q, mp, ackmp);
tp->tim_iocsave = NULL;
tp->tim_saved_prim = -1;
tp->tim_flags &= ~(WAITIOCACK | WAIT_IOCINFOACK |
TI_CAP_RECVD | CAP_WANTS_INFO);
}
static void
tim_send_ioctl_tpi_msg(queue_t *q, mblk_t *mp, struct tim_tim *tp,
struct iocblk *iocb)
{
mblk_t *tmp;
int ioc_cmd = iocb->ioc_cmd;
ASSERT(q != NULL && mp != NULL && tp != NULL);
ASSERT(q == WR(q));
ASSERT(mp->b_cont != NULL);
tp->tim_iocsave = mp;
tmp = mp->b_cont;
mp->b_cont = NULL;
tp->tim_flags |= WAITIOCACK;
tp->tim_saved_prim = ((union T_primitives *)tmp->b_rptr)->type;
if (ioc_cmd == TI_GETINFO || ioc_cmd == TI_SYNC ||
ioc_cmd == TI_CAPABILITY) {
tmp->b_datap->db_type = M_PCPROTO;
} else {
tmp->b_datap->db_type = M_PROTO;
}
ASSERT(iocb->ioc_cr == NULL || iocb->ioc_cr == DB_CRED(tmp));
ASSERT(DB_CRED(tmp) != NULL);
TILOG("timodwproc: sending down %d\n", tp->tim_saved_prim);
putnext(q, tmp);
}
static void
tim_clear_peer(struct tim_tim *tp)
{
mutex_enter(&tp->tim_mutex);
if (tp->tim_peercred != NULL) {
crfree(tp->tim_peercred);
tp->tim_peercred = NULL;
}
tp->tim_peerlen = 0;
mutex_exit(&tp->tim_mutex);
}