OpenSolaris_b135/uts/common/io/timod.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/* ONC_PLUS EXTRACT START */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


/*
 * Transport Interface Library cooperating module - issue 2
 */

/* ONC_PLUS EXTRACT END */
#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>

/*
 * This is the loadable module wrapper.
 */
#include <sys/conf.h>
#include <sys/modctl.h>

static struct streamtab timinfo;

static struct fmodsw fsw = {
	"timod",
	&timinfo,
	D_MTQPAIR | D_MP,
};

/*
 * Module linkage information for the kernel.
 */

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;

/*
 * This module keeps track of capabilities of underlying transport. Information
 * is persistent through module invocations (open/close). Currently it remembers
 * whether underlying transport supports TI_GET{MY,PEER}NAME ioctls and
 * T_CAPABILITY_REQ message. This module either passes ioctl/messages to the
 * transport or emulates it when transport doesn't understand these
 * ioctl/messages.
 *
 * It is assumed that transport supports T_CAPABILITY_REQ when timod receives
 * T_CAPABILITY_ACK from the transport. There is no current standard describing
 * transport behaviour when it receives unknown message type, so following
 * reactions are expected and handled:
 *
 * 1) Transport drops unknown T_CAPABILITY_REQ message type. In this case timod
 *    will wait for tcap_wait time and assume that transport doesn't provide
 *    this message type. T_CAPABILITY_REQ should never travel over the wire, so
 *    timeout value should only take into consideration internal processing time
 *    for the message. From user standpoint it may mean that an application will
 *    hang for TCAP_WAIT time in the kernel the first time this message is used
 *    with some particular transport (e.g. TCP/IP) during system uptime.
 *
 * 2) Transport responds with T_ERROR_ACK specifying T_CAPABILITY_REQ as
 *    original message type. In this case it is assumed that transport doesn't
 *    support it (which may not always be true - some transports return
 *    T_ERROR_ACK in other cases like lack of system memory).
 *
 * 3) Transport responds with M_ERROR, effectively shutting down the
 *    stream. Unfortunately there is no standard way to pass the reason of
 *    M_ERROR message back to the caller, so it is assumed that if M_ERROR was
 *    sent in response to T_CAPABILITY_REQ message, transport doesn't support
 *    it.
 *
 * It is possible under certain circumstances that timod will incorrectly assume
 * that underlying transport doesn't provide T_CAPABILITY_REQ message type. In
 * this "worst-case" scenario timod will emulate its functionality by itself and
 * will provide only TC1_INFO capability. All other bits in CAP_bits1 field are
 * cleaned. TC1_INFO is emulated by sending T_INFO_REQ down to transport
 * provider.
 */

/*
 * Notes about locking:
 *
 * tim_list_rwlock protects the list of tim_tim structures itself.  When this
 * lock is held, the list itself is stable, but the contents of the entries
 * themselves might not be.
 *
 * The rest of the members are generally protected by D_MTQPAIR, which
 * specifies a default exclusive inner perimeter.  If you're looking at
 * q->q_ptr, then it's stable.
 *
 * There's one exception to this rule: tim_peer{maxlen,len,name}.  These members
 * are touched without entering the associated STREAMS perimeter because we
 * get the pointer via tim_findlink() rather than q_ptr.  These are protected
 * by tim_mutex instead.  If you don't hold that lock, don't look at them.
 *
 * (It would be possible to separate out the 'set by T_CONN_RES' cases from the
 * others, but there appears to be no reason to do so.)
 */
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;
	/* Protected by the global tim_list_rwlock for all instances */
	struct tim_tim	*tim_next;
	struct tim_tim	**tim_ptpn;
	t_uscalar_t	tim_acceptor;
	t_scalar_t	tim_saved_prim;		/* Primitive from message */
						/*  part of ioctl. */
	timeout_id_t	tim_tcap_timoutid;	/* For T_CAP_REQ timeout */
	tpi_provinfo_t	*tim_provinfo;		/* Transport description */
	kmutex_t	tim_mutex;		/* protect tim_peer* */
	pid_t		tim_cpid;
};


/*
 * Local flags used with tim_flags field in instance structure of
 * type 'struct _ti_user' declared above.
 * Historical note:
 * This namespace constants were previously declared in a
 * a very messed up namespace in timod.h
 *
 * There may be 3 states for transport:
 *
 * 1) It provides T_CAPABILITY_REQ
 * 2) It does not provide T_CAPABILITY_REQ
 * 3) It is not known yet whether transport provides T_CAPABILITY_REQ or not.
 *
 * It is assumed that the underlying transport either provides
 * T_CAPABILITY_REQ or not and this does not changes during the
 * system lifetime.
 *
 */
#define	PEEK_RDQ_EXPIND 0x0001	/* look for expinds on stream rd queues */
#define	WAITIOCACK	0x0002	/* waiting for info for ioctl act	*/
#define	CLTS		0x0004	/* connectionless transport		*/
#define	COTS		0x0008	/* connection-oriented transport	*/
#define	CONNWAIT	0x0010	/* waiting for connect confirmation	*/
#define	LOCORDREL	0x0020	/* local end has orderly released	*/
#define	REMORDREL	0x0040	/* remote end had orderly released	*/
#define	NAMEPROC	0x0080	/* processing a NAME ioctl		*/
/* ONC_PLUS EXTRACT START */
#define	DO_MYNAME	0x0100	/* timod handles TI_GETMYNAME		*/
/* ONC_PLUS EXTRACT END */
#define	DO_PEERNAME	0x0200	/* timod handles TI_GETPEERNAME		*/
#define	TI_CAP_RECVD	0x0400	/* TI_CAPABILITY received		*/
#define	CAP_WANTS_INFO	0x0800	/* TI_CAPABILITY has TC1_INFO set	*/
#define	WAIT_IOCINFOACK	0x1000	/* T_INFO_REQ generated from ioctl	*/
#define	WAIT_CONNRESACK	0x2000	/* waiting for T_OK_ACK to T_CONN_RES	*/


/* Debugging facilities */
/*
 * Logging needed for debugging timod should only appear in DEBUG kernel.
 */
#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


/*
 * Sleep timeout for T_CAPABILITY_REQ. This message never travels across
 * network, so timeout value should be enough to cover all internal processing
 * time.
 */
clock_t tim_tcap_wait = 2;

/* Sleep timeout in tim_recover() */
#define	TIMWAIT	(1*hz)
/* Sleep timeout in tim_ioctl_retry() 0.2 seconds */
#define	TIMIOCWAIT	(200*hz/1000)

/*
 * Return values for ti_doname().
 */
#define	DONAME_FAIL	0	/* failing ioctl (done) */
#define	DONAME_DONE	1	/* done processing */
#define	DONAME_CONT	2	/* continue proceesing (not done yet) */

/*
 * Function prototypes
 */
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));
}


/*
 * Hash list for all instances. Used to find tim_tim structure based on
 * ACCEPTOR_id in T_CONN_RES. Protected by tim_list_rwlock.
 */
#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	/* _ILP32 */
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

/* ONC_PLUS EXTRACT START */
static int timodopen(queue_t *, dev_t *, int, int, cred_t *);
/* ONC_PLUS EXTRACT END */
static int timodclose(queue_t *, int, cred_t *);
static void timodwput(queue_t *, mblk_t *);
static void timodrput(queue_t *, mblk_t *);
/* ONC_PLUS EXTRACT START */
static void timodrsrv(queue_t *);
/* ONC_PLUS EXTRACT END */
static void timodwsrv(queue_t *);
/* ONC_PLUS EXTRACT START */
static int timodrproc(queue_t *, mblk_t *);
static int timodwproc(queue_t *, mblk_t *);
/* ONC_PLUS EXTRACT END */

/* stream data structure definitions */

static struct module_info timod_info =
	{TIMOD_ID, "timod", 0, INFPSZ, 512, 128};
static struct qinit timodrinit = {
	(int (*)())timodrput,
	(int (*)())timodrsrv,
	timodopen,
	timodclose,
	nulldev,
	&timod_info,
	NULL
};
static struct qinit timodwinit = {
	(int (*)())timodwput,
	(int (*)())timodwsrv,
	timodopen,
	timodclose,
	nulldev,
	&timod_info,
	NULL
};
static struct streamtab timinfo = { &timodrinit, &timodwinit, NULL, NULL };

/* ONC_PLUS EXTRACT START */
/*
 * timodopen -	open routine gets called when the module gets pushed
 *		onto the stream.
 */
/*ARGSUSED*/
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);

	/* Must be done before tpi_findprov and _ILP32 q_next walk below */
	qprocson(q);

	tp->tim_provinfo = tpi_findprov(q);

	/*
	 * Defer allocation of the buffers for the local address and
	 * the peer's address until we need them.
	 * Assume that timod has to handle getname until we here
	 * an iocack from the transport provider or we know that
	 * transport provider doesn't understand it.
	 */
	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;

		/*
		 * Find my driver's read queue (for T_CONN_RES handling)
		 */
		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	/* _ILP32 */

	/*
	 * Add this one to the list.
	 */
	tim_addlink(tp);

	/*
	 * Send M_SETOPTS to stream head to make sure M_PCPROTO messages
	 * are not flushed. This prevents application deadlocks.
	 */
	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);
}
/* ONC_PLUS EXTRACT END */

/*
 * timodclose - This routine gets called when the module gets popped
 * off of the stream.
 */
/*ARGSUSED*/
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);

	/*
	 * Cancel any outstanding bufcall
	 * or timeout requests.
	 */
	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);
}

/*
 * timodrput -	Module read put procedure.  This is called from
 *		the module, driver, or stream head upstream/downstream.
 *		Handles M_FLUSH, M_DATA and some M_PROTO (T_DATA_IND,
 *		and T_UNITDATA_IND) messages. All others are queued to
 *		be handled by the service procedures.
 */
static void
timodrput(queue_t *q, mblk_t *mp)
{
	union T_primitives *pptr;

	/*
	 * During flow control and other instances when messages
	 * are on queue, queue up a non high priority message
	 */
	if (q->q_first != 0 && mp->b_datap->db_type < QPCTL) {
		(void) putq(q, mp);
		return;
	}

	/*
	 * Inline processing of data (to avoid additional procedure call).
	 * Rest is handled in timodrproc.
	 */

	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;
	}
}

/* ONC_PLUS EXTRACT START */
/*
 * timodrsrv -	Module read queue service procedure.  This is called when
 *		messages are placed on an empty queue, when high priority
 *		messages are placed on the queue, and when flow control
 *		restrictions subside.  This code used to be included in a
 *		put procedure, but it was moved to a service procedure
 *		because several points were added where memory allocation
 *		could fail, and there is no reasonable recovery mechanism
 *		from the put procedure.
 */
/*ARGSUSED*/
static void
timodrsrv(queue_t *q)
{
/* ONC_PLUS EXTRACT END */
	mblk_t *mp;
	struct tim_tim *tp;

	ASSERT(q != NULL);

	tp = (struct tim_tim *)q->q_ptr;
	if (!tp)
		return;

	while ((mp = getq(q)) != NULL) {
		if (timodrproc(q, mp)) {
			/*
			 * timodrproc did a putbq - stop processing
			 * messages.
			 */
			return;
		}
	}
/* ONC_PLUS EXTRACT START */
}

/*
 * Perform common processing when a T_CAPABILITY_ACK or T_INFO_ACK
 * arrive.  Set the queue properties and adjust the tim_flags according
 * to the service type.
 */
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)
{
	union T_primitives *pptr;
	struct tim_tim *tp;
	struct iocblk *iocbp;
	mblk_t *nbp;
	size_t blen;
/* ONC_PLUS EXTRACT END */

	tp = (struct tim_tim *)q->q_ptr;

/* ONC_PLUS EXTRACT START */
	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);
		/*
		 * There is no specified standard response for driver when it
		 * receives unknown message type and M_ERROR is one
		 * possibility. If we send T_CAPABILITY_REQ down and transport
		 * provider responds with M_ERROR we assume that it doesn't
		 * understand this message type. This assumption may be
		 * sometimes incorrect (transport may reply with M_ERROR for
		 * some other reason) but there is no way for us to distinguish
		 * between different cases. In the worst case timod and everyone
		 * else sharing global transport description with it may end up
		 * emulating T_CAPABILITY_REQ.
		 */

		/*
		 * Check that we are waiting for T_CAPABILITY_ACK and
		 * T_CAPABILITY_REQ is not implemented by transport or emulated
		 * by timod.
		 */
		if ((tp->tim_provinfo->tpi_capability == PI_DONTKNOW) &&
		    ((tp->tim_flags & TI_CAP_RECVD) != 0)) {
			/*
			 * Good chances that this transport doesn't provide
			 * T_CAPABILITY_REQ. Mark this information  permanently
			 * for the module + transport combination.
			 */
			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)) {
			/*
			 * Note: it's not actually possible to get
			 * here with db_type M_PCPROTO, because
			 * timodrput has already checked MBLKL, and
			 * thus the assertion below.  If the length
			 * was too short, then the message would have
			 * already been putnext'd, and would thus
			 * never appear here.  Just the same, the code
			 * below handles the impossible case since
			 * it's easy to do and saves future
			 * maintainers from unfortunate accidents.
			 */
			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:
/* ONC_PLUS EXTRACT END */

			if (audit_active)
				audit_sock(T_UNITDATA_IND, q, mp, TIMOD_ID);
/* ONC_PLUS EXTRACT START */
			putnext(q, mp);
			break;
/* ONC_PLUS EXTRACT END */

		case T_ERROR_ACK:
			/* Restore db_type - recover() might have changed it */
			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;
				/* true as message is put on list */
				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;

/* ONC_PLUS EXTRACT START */
		case T_BIND_ACK: {
			struct T_bind_ack *ackp =
			    (struct T_bind_ack *)mp->b_rptr;

			/* Restore db_type - recover() might have changed it */
			mp->b_datap->db_type = M_PCPROTO;
			if (blen < sizeof (*ackp)) {
				putnext(q, mp);
				break;
			}

			/* save negotiated backlog */
			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 != NULL) {
						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;
		}

/* ONC_PLUS EXTRACT END */
		case T_OPTMGMT_ACK:

		tilog("timodrproc: Got T_OPTMGMT_ACK\n", 0);

		/* Restore db_type - recover() might have change it */
		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;

		/* Restore db_type - recover() might have changed it */
		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;

			/*
			 * The only case when T_INFO_ACK may be received back
			 * when we are waiting for ioctl to complete is when
			 * this ioctl sent T_INFO_REQ down.
			 */
			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;

			/*
			 * Was it sent from TI_CAPABILITY emulation?
			 */
			if (ioc_cmd == TI_CAPABILITY) {
				struct T_info_ack	saved_info;

				/*
				 * Perform sanity checks. The only case when we
				 * send T_INFO_REQ from TI_CAPABILITY is when
				 * timod emulates T_CAPABILITY_REQ and CAP_bits1
				 * has TC1_INFO set.
				 */
				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);

				/* Save info & reuse mp for T_CAPABILITY_ACK */
				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);
				}

				/*
				 * Copy T_INFO information into T_CAPABILITY_ACK
				 */
				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;
			}

			/*
			 * The code for TI_SYNC/TI_GETINFO is left here only for
			 * backward compatibility with staticaly linked old
			 * applications. New TLI/XTI code should use
			 * TI_CAPABILITY for getting transport info and should
			 * not use TI_GETINFO/TI_SYNC for this purpose.
			 */

			/*
			 * make sure the message sent back is the size of
			 * the "expected ack"
			 * For TI_GETINFO, expected ack size is
			 *	sizeof (T_info_ack)
			 * For TI_SYNC, expected ack size is
			 *	sizeof (struct ti_sync_ack);
			 */
			if (ioc_cmd != TI_GETINFO && ioc_cmd != TI_SYNC) {
				putnext(q, mp);
				break;
			}

			expected_ack_size =
			    sizeof (struct T_info_ack); /* TI_GETINFO */
			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;
				    }
				}
			}
			/*
			 * We now have "mp" which has enough space for an
			 * appropriate ack and contains struct T_info_ack
			 * that the transport provider returned. We now
			 * stuff it with more stuff to fullfill
			 * TI_SYNC ioctl needs, as necessary
			 */
			if (iocbp->ioc_cmd == TI_SYNC) {
				/*
				 * Assumes struct T_info_ack is first embedded
				 * type in struct ti_sync_ack so it is
				 * automatically there.
				 */
				struct ti_sync_ack *tsap =
				    (struct ti_sync_ack *)mp->b_rptr;

				/*
				 * tsap->tsa_qlen needs to be set only if
				 * TSRF_QLEN_REQ flag is set, but for
				 * compatibility with statically linked
				 * applications it is set here regardless of the
				 * flag since old XTI library expected it to be
				 * set.
				 */
				tsap->tsa_qlen = tp->tim_backlog;
				tsap->tsa_flags = 0x0; /* intialize clear */
				if (tp->tim_flags & PEEK_RDQ_EXPIND) {
					/*
					 * Request to peek for EXPIND in
					 * rcvbuf.
					 */
					if (ti_expind_on_rdqueues(q)) {
						/*
						 * Expedited data is
						 * queued on the stream
						 * read side
						 */
						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;

/* ONC_PLUS EXTRACT START */
		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);
				}
			}
/* ONC_PLUS EXTRACT END */
			if (audit_active)
				audit_sock(T_CONN_IND, q, mp, TIMOD_ID);
/* ONC_PLUS EXTRACT START */
			putnext(q, mp);
			break;
		}

/* ONC_PLUS EXTRACT END */
	    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;
			}

			/* This transport supports T_CAPABILITY_REQ */
			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);

			/* Reset possible pending timeout */
			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;

/* ONC_PLUS EXTRACT START */
	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;
/* ONC_PLUS EXTRACT END */

	case M_IOCACK:
	    iocbp = (struct iocblk *)mp->b_rptr;

	    tilog("timodrproc: Got M_IOCACK\n", 0);

	    if (iocbp->ioc_cmd == TI_GETMYNAME) {

		/*
		 * Transport provider supports this ioctl,
		 * so I don't have to.
		 */
		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;
		}
		/* tim_iocsave may already be overwritten. */
		if (tp->tim_saved_prim == -1) {
			freemsg(tp->tim_iocsave);
			tp->tim_iocsave = NULL;
		}
	    } else if (iocbp->ioc_cmd == TI_GETPEERNAME) {
		boolean_t clearit;

		/*
		 * Transport provider supports this ioctl,
		 * so I don't have to.
		 */
		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;
		}
		/* tim_iocsave may already be overwritten. */
		if (tp->tim_saved_prim == -1) {
			freemsg(tp->tim_iocsave);
			tp->tim_iocsave = NULL;
		}
	    }
	    putnext(q, mp);
	    break;

/* ONC_PLUS EXTRACT START */
	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);
		    /* tim_iocsave may already be overwritten. */
		    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;
/* ONC_PLUS EXTRACT END */
	}

	return (0);
}

/* ONC_PLUS EXTRACT START */
/*
 * timodwput -	Module write put procedure.  This is called from
 *		the module, driver, or stream head upstream/downstream.
 *		Handles M_FLUSH, M_DATA and some M_PROTO (T_DATA_REQ,
 *		and T_UNITDATA_REQ) messages. All others are queued to
 *		be handled by the service procedures.
 */

static void
timodwput(queue_t *q, mblk_t *mp)
{
	union T_primitives *pptr;
	struct tim_tim *tp;
	struct iocblk *iocbp;

	/*
	 * Enqueue normal-priority messages if our queue already
	 * holds some messages for deferred processing but don't
	 * enqueue those M_IOCTLs which will result in an
	 * M_PCPROTO (ie, high priority) message being created.
	 */
/* ONC_PLUS EXTRACT END */
	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;

			case TI_GETINFO:
			case TI_SYNC:
			case TI_CAPABILITY:
				break;
			}
		} else {
			(void) putq(q, mp);
			return;
		}
	}
/* ONC_PLUS EXTRACT START */
	/*
	 * Inline processing of data (to avoid additional procedure call).
	 * Rest is handled in timodwproc.
	 */

	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) {
/* ONC_PLUS EXTRACT END */
		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;
/* ONC_PLUS EXTRACT START */
	default:
		(void) timodwproc(q, mp);
		break;
	}
}
/*
 * timodwsrv -	Module write queue service procedure.
 *		This is called when messages are placed on an empty queue,
 *		when high priority messages are placed on the queue, and
 *		when flow control restrictions subside.  This code used to
 *		be included in a put procedure, but it was moved to a
 *		service procedure because several points were added where
 *		memory allocation could fail, and there is no reasonable
 *		recovery mechanism from the put procedure.
 */
static void
timodwsrv(queue_t *q)
{
	mblk_t *mp;

	ASSERT(q != NULL);
	if (q->q_ptr == NULL)
		return;

	while ((mp = getq(q)) != NULL) {
		if (timodwproc(q, mp)) {
			/*
			 * timodwproc did a putbq - stop processing
			 * messages.
			 */
			return;
		}
	}
}

/*
 * Common routine to process write side messages
 */

static int
timodwproc(queue_t *q, mblk_t *mp)
{
	union T_primitives *pptr;
	struct tim_tim *tp;
	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;
/* ONC_PLUS EXTRACT END */

	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;

/* ONC_PLUS EXTRACT START */
	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));

		/*
		 * TPI requires we await response to a previously sent message
		 * before handling another, put it back on the head of queue.
		 * Since putbq() may see QWANTR unset when called from the
		 * service procedure, the queue must be explicitly scheduled
		 * for service, as no backenable will occur for this case.
		 * tim_ioctl_retry() sets a timer to handle the qenable.
		 */
		if (tp->tim_flags & WAITIOCACK) {
			TILOG("timodwproc: putbq M_IOCTL(%d)\n",
			    iocbp->ioc_cmd);
			(void) putbq(q, mp);
			/* Called from timodwsrv() and messages on queue */
			if (!(q->q_flag & QWANTR))
				tim_ioctl_retry(q);
			return (1);
		}
/* ONC_PLUS EXTRACT END */

		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);

			/*
			 * We know that tim_send_ioctl_tpi_msg() is only
			 * going to examine the `type' field, so we only
			 * check that we can access that much data.
			 */
			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);

			/*
			 * Save out the value of tsr_flags, in case we
			 * reallocb() tsr_mp (below).
			 */
			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);

				/* Can reply immediately. */
				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;
			}

			/*
			 * This code is retained for compatibility with
			 * old statically linked applications. New code
			 * should use TI_CAPABILITY for all TPI
			 * information and should not use TSRF_INFO_REQ
			 * flag.
			 *
			 * defer processsing necessary to rput procedure
			 * as we need to get information from transport
			 * driver. Set flags that will tell the read
			 * side the work needed on this request.
			 */

			if (tsr_flags & TSRF_IS_EXP_IN_RCVBUF)
				tp->tim_flags |= PEEK_RDQ_EXPIND;

			/*
			 * Convert message to a T_INFO_REQ message; relies
			 * on sizeof (struct ti_sync_req) >= sizeof (struct
			 * T_info_req)).
			 */
			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:
				/* Just send T_CAPABILITY_REQ down */
				tim_send_ioctl_tpi_msg(q, mp, tp, iocbp);
				break;

			case PI_DONTKNOW:
				/*
				 * It is unknown yet whether transport provides
				 * T_CAPABILITY_REQ or not. Send message down
				 * and wait for reply.
				 */

				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:
				/*
				 * Transport doesn't support T_CAPABILITY_REQ.
				 * Either reply immediately or send T_INFO_REQ
				 * if needed.
				 */
				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);

				/*
				 * Generate T_INFO_REQ message and send
				 * it down
				 */
					((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;
				}


				/*
				 * Can reply immediately. Just send back
				 * T_CAPABILITY_ACK with CAP_bits1 set to 0.
				 */
				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);

				/*
				 * It could happen when timod is awaiting ack
				 * for TI_GETPEERNAME/TI_GETMYNAME.
				 */
				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;

/* ONC_PLUS EXTRACT START */
		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);
			}
			/*
			 * tim_iocsave may be non-NULL when timod is awaiting
			 * ack for another TI_GETPEERNAME/TI_GETMYNAME.
			 */
			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;
/* ONC_PLUS EXTRACT END */

		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 (audit_active)
				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;

/* ONC_PLUS EXTRACT START */
		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;
/* ONC_PLUS EXTRACT END */
			if (audit_active)
				audit_sock(T_CONN_REQ, q, mp, TIMOD_ID);
/* ONC_PLUS EXTRACT START */
		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;

			/*
			 * Construct a list with:
			 *	nbp - copy of user's original request
			 *	tmp - the extracted T_conn_ind
			 */
			nbp->b_cont = tmp;
			/*
			 * tim_iocsave may be non-NULL when timod is awaiting
			 * ack for TI_GETPEERNAME/TI_GETMYNAME.
			 */
			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;
		}

/* ONC_PLUS EXTRACT END */
		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);

			/*
			 * If we are already connected, there won't
			 * be any messages on tim_consave.
			 */
			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);
			/*
			 * XXX: We may know at this point whether transport
			 * provides T_CAPABILITY_REQ or not and we may utilise
			 * this knowledge here.
			 */
			putnext(q, mp);
			break;
/* ONC_PLUS EXTRACT START */
		}
		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);
	}
}


/*
 * Process the TI_GETNAME ioctl.  If no name exists, return len = 0
 * in strbuf structures.  The state transitions are determined by what
 * is hung of cq_private (cp_private) in the copyresp (copyreq) structure.
 * The high-level steps in the ioctl processing are as follows:
 *
 * 1) we recieve an transparent M_IOCTL with the arg in the second message
 *	block of the message.
 * 2) we send up an M_COPYIN request for the strbuf structure pointed to
 *	by arg.  The block containing arg is hung off cq_private.
 * 3) we receive an M_IOCDATA response with cp->cp_private->b_cont == NULL.
 *	This means that the strbuf structure is found in the message block
 *	mp->b_cont.
 * 4) we send up an M_COPYOUT request with the strbuf message hung off
 *	cq_private->b_cont.  The address we are copying to is strbuf.buf.
 *	we set strbuf.len to 0 to indicate that we should copy the strbuf
 *	structure the next time.  The message mp->b_cont contains the
 *	address info.
 * 5) we receive an M_IOCDATA with cp_private->b_cont != NULL and
 *	strbuf.len == 0.  Restore strbuf.len to either tp->tim_mylen or
 *	tp->tim_peerlen.
 * 6) we send up an M_COPYOUT request with a copy of the strbuf message
 *	hung off mp->b_cont.  In the strbuf structure in the message hung
 *	off cq_private->b_cont, we set strbuf.len to 0 and strbuf.maxlen
 *	to 0.  This means that the next step is to ACK the ioctl.
 * 7) we receive an M_IOCDATA message with cp_private->b_cont != NULL and
 *	strbuf.len == 0 and strbuf.maxlen == 0.  Free up cp->private and
 *	send an M_IOCACK upstream, and we are done.
 *
 */
static int
ti_doname(
	queue_t *q,		/* queue message arrived at */
	mblk_t *mp)		/* M_IOCTL or M_IOCDATA message only */
{
	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) {	/* error */
			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) {	/* got strbuf */
			ASSERT(mp->b_cont);
			STRUCT_SET_HANDLE(sb, iocp->ioc_flag,
			    (void *)mp->b_cont->b_rptr);
			if (getpeer) {
				if (tp->tim_peerlen == 0) {
					/* copy just strbuf */
					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 {
					/* copy buffer */
					STRUCT_FSET(sb, len, tp->tim_peerlen);
				}
			} else {
				if (tp->tim_mylen == 0) {
					/* copy just strbuf */
					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 {
					/* copy buffer */
					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) {
			/*
			 * restore strbuf.len
			 */
			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) {

				/*
				 * ack the ioctl
				 */
				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); /* ack next time around */
			qreply(q, mp);
			ret = DONAME_CONT;
			break;
		}

		/*
		 * copy the address to the user
		 */
		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); /* copy the strbuf next time around */
		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);
}

/* ONC_PLUS EXTRACT END */

/*
 * Fill in the address of a connectionless data packet if a connect
 * had been done on this endpoint.
 */
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);
}

/* ONC_PLUS EXTRACT START */
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;

	/*
	 * Avoid re-enabling the queue.
	 */
	if (mp->b_datap->db_type == M_PCPROTO)
		mp->b_datap->db_type = M_PROTO;
	noenable(q);
	(void) putbq(q, mp);

	/*
	 * Make sure there is at most one outstanding request per queue.
	 */
	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;
	}
}

/*
 * Timod is waiting on a downstream ioctl reply, come back soon
 * to reschedule the write side service routine, which will check
 * if the ioctl is done and another can proceed.
 */
static void
tim_ioctl_retry(queue_t *q)
{
	struct tim_tim  *tp;

	tp = (struct tim_tim *)q->q_ptr;

	/*
	 * Make sure there is at most one outstanding request per wqueue.
	 */
	if (tp->tim_wtimoutid || tp->tim_wbufcid)
		return;

	tp->tim_wtimoutid = qtimeout(RD(q), tim_timer, q, TIMIOCWAIT);
}

/*
 * Inspect the data on read queues starting from read queues passed as
 * paramter (timod read queue) and traverse until
 * q_next is NULL (stream head). Look for a TPI T_EXDATA_IND message
 * reutrn 1 if found, 0 if not found.
 */
static int
ti_expind_on_rdqueues(queue_t *rq)
{
	mblk_t *bp;
	queue_t *q;

	q = rq;
	/*
	 * We are going to walk q_next, so protect stream from plumbing
	 * changes.
	 */
	claimstr(q);
	do {
		/*
		 * Hold QLOCK while referencing data on queues
		 */
		mutex_enter(QLOCK(rq));
		bp = rq->q_first;
		while (bp != NULL) {
			/*
			 * Walk the messages on the queue looking
			 * for a possible T_EXDATA_IND
			 */
			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)) {
				/* bp is T_EXDATA_IND */
				mutex_exit(QLOCK(rq));
				releasestr(q); /* decrement sd_refcnt  */
				return (1); /* expdata is on a read queue */
			}
			bp = bp->b_next; /* next message */
		}
		mutex_exit(QLOCK(rq));
		rq = rq->q_next;	/* next upstream queue */
	} while (rq != NULL);
	releasestr(q);
	return (0);		/* no expdata on read queues */
}

/* ONC_PLUS EXTRACT END */
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);
}

/*
 * tim_tcap_genreply() is called either from timeout routine or when
 * T_ERROR_ACK is received. In both cases it means that underlying
 * transport doesn't provide T_CAPABILITY_REQ.
 */
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);

	/* Save this information permanently in the module */
	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) {
		/* Send T_INFO_REQ down */
		mblk_t *tirmp = tpi_ack_alloc(NULL,
		    sizeof (struct T_info_req), M_PCPROTO, T_INFO_REQ);

		if (tirmp != NULL) {
			/* Emulate TC1_INFO */
			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 {
		/* Reply immediately */
		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) {
		/* It is safe to call freemsg for NULL pointers */
		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;
	/*
	 * All ioctl's may return more data than was specified by
	 * count arg. For TI_CAPABILITY count is treated as maximum data size.
	 */
	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);
		/* Truncate message if too large */
		mp->b_wptr = mp->b_rptr + iocbp->ioc_count;
	}

	TILOG("iosendreply: ioc_cmd = %d, ", iocbp->ioc_cmd);
	putnext(RD(q), ioc_mp);
}

/*
 * Send M_IOCACK for errors.
 */
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);

	/* Always send this to the read side of the queue */
	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);

			/* get saved ioctl msg and set values */
			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;
		}
	}
}

/*
 * Send reply to a usual message or ioctl message upstream.
 * Should be called from the read side only.
 */
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));

	/* Restore db_type - recover() might have changed it */
	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);
	}
}

/*
 * Reply to TI_SYNC reequest without sending anything downstream.
 */
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) {
		/*
		 * unsupported/bad flag setting
		 * or no flag set.
		 */
		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))) {
		/*
		 * Expedited data is queued on
		 * the stream read side
		 */
		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);
}

/*
 * Send TPI message from IOCTL message, ssave original ioctl header and TPI
 * message type. Should be called from write side only.
 */
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;

	/*
	 * For TI_GETINFO, the attached message is a T_INFO_REQ
	 * For TI_SYNC, we generate the T_INFO_REQ message above
	 * For TI_CAPABILITY the attached message is either
	 * T_CAPABILITY_REQ or T_INFO_REQ.
	 * Among TPI request messages possible,
	 *	T_INFO_REQ/T_CAPABILITY_ACK messages are a M_PCPROTO, rest
	 *	are M_PROTO
	 */
	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;
	}

	/* Verify credentials in STREAM */
	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);
}