/*****************************************************************************

 @(#) inet.c,v SS7preBetaRelease(0.8.2.7) 2002/04/13 19:27:40

 -----------------------------------------------------------------------------

 Copyright (c) 2001-2002  OpenSS7 Corporation <http://www.openss7.com>
 Copyright (c) 1997-2000  Brian F. G. Bidulock <bidulock@dallas.net>

 All Rights Reserved.

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 675 Mass
 Ave, Cambridge, MA 02139, USA.

 -----------------------------------------------------------------------------

 U.S. GOVERNMENT RESTRICTED RIGHTS.  If you are licensing this Software on
 behalf of the U.S. Government ("Government"), the following provisions apply
 to you.  If the Software is supplied by the Department of Defense ("DoD"), it
 is classified as "Commercial Computer Software" under paragraph 252.227-7014
 of the DoD Supplement to the Federal Acquisition Regulations ("DFARS") (or any
 successor regulations) and the Government is acquiring only the license rights
 granted herein (the license rights customarily provided to non-Government
 users).  If the Software is supplied to any unit or agency of the Government
 other than DoD, it is classified as "Restricted Computer Software" and the
 Government's rights in the Software are defined in paragraph 52.227-19 of the
 Federal Acquisition Regulations ("FAR") (or any success regulations) or, in
 the cases of NASA, in paragraph 18.52.227-86 of the NASA Supplement to the FAR
 (or any successor regulations).

 -----------------------------------------------------------------------------

 Commercial licensing and support of this software is available from OpenSS7
 Corporation at a fee.  See http://www.openss7.com/

 -----------------------------------------------------------------------------

 Last Modified 2002/04/13 19:27:40 by brian

 *****************************************************************************/

#ident "@(#) inet.c,v SS7preBetaRelease(0.8.2.7) 2002/04/13 19:27:40"

static char const ident[] =
    "inet.c,v SS7preBetaRelease(0.8.2.7) 2002/04/13 19:27:40";

/* 
 *  This driver provides the functionality of IP (Internet Protocol) over a
 *  connectionless network provider (NPI).  It provides a STREAMS-based
 *  encapsulation of the Linux IP stack.
 */

#include <linux/config.h>
#include <linux/version.h>
#ifdef CONFIG_MODVERSIONS
#undef  MODVERSIONS
#define MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>

#include <linux/net.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <net/sock.h>

#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/cmn_err.h>

#include <sys/tpi.h>
#include <sys/tpi_inet.h>
#include <sys/xti_inet.h>

#include "../lock.h"
#include "../debug.h"
#include "../bufq.h"

#define	INET_DESCRIP	"INET STREAMS (TPI) DRIVER." "\n" \
			"Part of the OpenSS7 Stack for LiS STREAMS."
#define INET_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  All Rights Reserved."
#define INET_DEVICE	"Supports OpenSS7 INET Drivers."
#define INET_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define INET_BANNER	INET_DESCRIP	"\n" \
			INET_COPYRIGHT	"\n" \
			INET_DEVICE	"\n" \
			INET_CONTACT	"\n"

MODULE_AUTHOR(INET_CONTACT);
MODULE_DESCRIPTION(INET_DESCRIP);
MODULE_SUPPORTED_DEVICE(INET_DEVICE);

#ifndef INET_CMAJOR
#define INET_CMAJOR	30
#define IP_CMINOR	32
#define ICMP_CMINOR	33
#define GGP_CMINOR	34
#define IPIP_CMINOR	35
#define TCP_CMINOR	36
#define EGP_CMINOR	37
#define PUP_CMINOR	38
#define UDP_CMINOR	39
#define IDP_CMINOR	40
#define RAWIP_CMINOR	41
#define FREE_CMINOR	50
#endif

#ifndef INET_NMAJOR
#define INET_NMAJOR 1
#define INET_NMINOR 255
#endif

#ifndef INET_IOC_MAGIC
#define INET_IOC_MAGIC 'i'
#endif

#define INET_MODULE_ID ('I'<<8|INET_IOC_MAGIC)

#ifdef LINUX_2_4
#define INT int
#else
#define INT void
#endif

/* 
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */
STATIC struct module_info inet_minfo = {
	mi_idnum:0,		/* Module ID number */
	mi_idname:"socksys",	/* Module name */
	mi_minpsz:0,		/* Min packet size accepted */
	mi_maxpsz:INFPSZ,	/* Max packet size accepted */
	mi_hiwat:1 << 15,	/* Hi water mark */
	mi_lowat:1 << 10	/* Lo water mark */
};

STATIC int inet_open(queue_t *, dev_t *, int, int, cred_t *);
STATIC int inet_close(queue_t *, int, cred_t *);

STATIC INT inet_rput(queue_t *, mblk_t *);
STATIC INT inet_rsrv(queue_t *);

STATIC struct qinit inet_rinit = {
	qi_putp:inet_rput,	/* Read put (msg from below) */
	qi_srvp:inet_rsrv,	/* Read queue service */
	qi_qopen:inet_open,	/* Each open */
	qi_qclose:inet_close,	/* Last close */
	qi_qadmin:NULL,		/* Admin (not used) */
	qi_minfo:&inet_minfo,	/* Information */
	qi_mstat:NULL		/* Statistics */
};

STATIC INT inet_wput(queue_t *, mblk_t *);
STATIC INT inet_wsrv(queue_t *);

STATIC struct qinit inet_winit = {
	qi_putp:inet_wput,	/* Write put (msg from above) */
	qi_srvp:inet_wsrv,	/* Write queue service */
	qi_qopen:NULL,		/* Each open */
	qi_qclose:NULL,		/* Last close */
	qi_qadmin:NULL,		/* Admin (not used) */
	qi_minfo:&inet_minfo,	/* Information */
	qi_mstat:NULL		/* Statistics */
};

STATIC struct streamtab inet_info = {
	st_rdinit:&inet_rinit,	/* Upper read queue */
	st_wrinit:&inet_winit,	/* Lower read queue */
	st_muxrinit:NULL,	/* Lower read queue */
	st_muxwinit:NULL	/* Lower write queue */
};

/*
 *  Queue put and service return values
 */
#define QR_DONE		0
#define QR_ABSORBED	1
#define QR_TRIMMED	2
#define QR_LOOP		3
#define QR_PASSALONG	4
#define QR_PASSFLOW	5
#define QR_DISABLE	6

/* 
 *  TLI interface state flags
 */
#define TSF_UNBND	( 1 << TS_UNBND		)
#define TSF_WACK_BREQ	( 1 << TS_WACK_BREQ	)
#define TSF_WACK_UREQ	( 1 << TS_WACK_UREQ	)
#define TSF_IDLE	( 1 << TS_IDLE		)
#ifdef TS_WACK_OPTREQ
#define TSF_WACK_OPTREQ	( 1 << TS_WACK_OPTREQ	)
#endif
#define TSF_WACK_CREQ	( 1 << TS_WACK_CREQ	)
#define TSF_WCON_CREQ	( 1 << TS_WCON_CREQ	)
#define TSF_WRES_CIND	( 1 << TS_WRES_CIND	)
#define TSF_WACK_CRES	( 1 << TS_WACK_CRES	)
#define TSF_DATA_XFER	( 1 << TS_DATA_XFER	)
#define TSF_WIND_ORDREL	( 1 << TS_WIND_ORDREL	)
#define TSF_WREQ_ORDREL	( 1 << TS_WREQ_ORDREL	)
#define TSF_WACK_DREQ6	( 1 << TS_WACK_DREQ6	)
#define TSF_WACK_DREQ7	( 1 << TS_WACK_DREQ7	)
#define TSF_WACK_DREQ9	( 1 << TS_WACK_DREQ9	)
#define TSF_WACK_DREQ10	( 1 << TS_WACK_DREQ10	)
#define TSF_WACK_DREQ11	( 1 << TS_WACK_DREQ11	)
#define TSF_NOSTATES	( 1 << TS_NOSTATES	)

#define TSM_WACK_DREQ	(TSF_WACK_DREQ6 \
			|TSF_WACK_DREQ7 \
			|TSF_WACK_DREQ9 \
			|TSF_WACK_DREQ10 \
			|TSF_WACK_DREQ11)

#define TSM_LISTEN	(TSF_IDLE \
			|TSF_WRES_CIND)

#define TSM_CONNECTED	(TSF_WCON_CREQ\
			|TSF_WRES_CIND\
			|TSF_DATA_XFER\
			|TSF_WIND_ORDREL\
			|TSF_WREQ_ORDREL)

#define TSM_DISCONN	(TSF_IDLE\
			|TSF_UNBND)

#define TSM_INDATA	(TSF_DATA_XFER\
			|TSF_WIND_ORDREL)

#define TSM_OUTDATA	(TSF_DATA_XFER\
			|TSF_WREQ_ORDREL)

#ifndef T_PROVIDER
#define T_PROVIDER  0
#define T_USER	    1
#endif

/*
 *  Socket state masks
 */
/*
 *  TCP state masks
 */
#define TCPM_CLOSING	(TCPF_CLOSE\
			|TCPF_TIME_WAIT\
			|TCPF_CLOSE_WAIT)

#define TCPM_CONNIND	(TCPF_SYN_RECV\
			|TCPF_ESTABLISHED\
			|TCPF_LISTEN)

/* 
 *  =========================================================================
 *
 *  IP Private Datastructures
 *
 *  =========================================================================
 */
typedef struct sockaddr_in inet_addr_t;

typedef struct ops {
	int (*r_prim) (queue_t *, mblk_t *);
	int (*w_prim) (queue_t *, mblk_t *);
} ops_t;

typedef struct inet_dflt {
	struct ip_options opt;	/* T_IP_OPTIONS */
	ulong tos;		/* T_IP_TOS */
	ulong ttl;		/* T_IP_TTL */
	ulong reuse;		/* T_IP_REUSE_ADDR */
	ulong norte;		/* T_IP_DONTROUTE */
	ulong bcast;		/* T_IP_BROADCAST */
	ulong nodly;		/* T_TCP_NODELAY */
	ulong mss;		/* T_TCP_MAXSEG */
	ulong alive;		/* T_TCP_KEEPALIVE */
	ulong csum;		/* T_UDP_CHECKSUM */
} inet_dflt_t;

typedef struct inet {
	struct inet *next;	/* list of all IP-Users */
	struct inet **prev;	/* list of all IP-Users */
	struct ops ops;		/* operations */
	queue_t *rq;		/* associated read queue */
	queue_t *wq;		/* associated write queue */
	dev_t devid;		/* device id at open */
	cred_t cred;		/* credientials */
	lis_spin_lock_t lock;	/* queue lock */
	void * users;		/* concurrent users */
	uint flags;		/* interface flags */
	uint state;		/* interface state */
	uint family;		/* protocol family */
	uint type;		/* socket type */
	uint proto;		/* bound protocol id */
	inet_addr_t src;	/* bound address */
	inet_addr_t dst;	/* connected address */
	inet_dflt_t options;	/* protocol options */
	unsigned char _pad[40];	/* pad for options */
	uint rbid;		/* RD buffer call id */
	uint wbid;		/* WR buffer call id */
	bufq_t conq;		/* connection queue */
	uint conind;		/* number of connection indications */
	size_t tsdu;		/* tsdu size */
	size_t etsdu;		/* etsdu size */
	size_t cdata;		/* cdata size */
	size_t ddata;		/* ddata size */
	size_t asize;		/* max address size */
	size_t osize;		/* max options size */
	size_t tidu;		/* interface data unit size */
	uint serv_type;		/* service type */
	uint prov_flags;	/* provider flags */
	uint mtu;		/* MTU for this connection */
	struct socket *sock;	/* socket pointer */
} inet_t;

#define INET_PRIV(__q) ((inet_t *)((__q)->q_ptr))
#define inet_init_lock(__q) lis_spin_lock_init(&(INET_PRIV(__q))->lock,"inet-private")
#define inet_locked(__q) ((INET_PRIV(__q))->users)

STATIC INLINE int inet_trylock(queue_t * q)
{
	inet_t *ip = INET_PRIV(q);
	lis_spin_lock_t *lock = &ip->lock;

	lis_spin_lock_bh(lock);
	if (ip->users && ip->users != current) {
		lis_spin_unlock_bh(lock);
		return (!0);
	}
	ip->users = current;
	lis_spin_unlock_bh(lock);
	return (0);
}

STATIC INLINE void inet_wakeup(queue_t * q);

STATIC INLINE void inet_unlock(queue_t * q)
{
	inet_t *ip = INET_PRIV(q);
	lis_spin_lock_t *lock = &ip->lock;

	inet_wakeup(q);
	lis_spin_lock_bh(lock);
	if (ip->users && ip->users == current)
		ip->users = NULL;
	else
		ptrace(("Double unlock\n"));
	lis_spin_unlock_bh(lock);
}

typedef struct inet_opts {
	uint flags;		/* success flags */
	struct ip_options *opt;	/* T_IP_OPTIONS */
	ulong *tos;		/* T_IP_TOS */
	ulong *ttl;		/* T_IP_TTL */
	ulong *reuse;		/* T_IP_REUSE_ADDR */
	ulong *norte;		/* T_IP_DONTROUTE */
	ulong *bcast;		/* T_IP_BROADCAST */
	ulong *nodly;		/* T_TCP_NODELAY */
	ulong *mss;		/* T_TCP_MAXSEG */
	ulong *alive;		/* T_TCP_KEEPALIVE */
	ulong *csum;		/* T_UDP_CHECKSUM */
	ulong *ropt;
	struct in_pktinfo *pkinfo;
} inet_opts_t;

#define ip_default_opt		{ 0, }
#define ip_default_tos		1
#define ip_default_ttl		64
#define ip_default_reuse	1
#define ip_default_norte	1
#define ip_default_bcast	1
#define tcp_default_nodly	1
#define tcp_default_mss		576
#define tcp_default_alive	1
#define udp_default_csum	0

#define TF_IP_OPTIONS		0x1
#define TF_IP_TOS		0x2
#define TF_IP_TTL		0x4
#define TF_IP_REUSEADDR		0x8
#define TF_IP_DONTROUTE		0x10
#define TF_IP_BROADCAST		0x20
#define TF_IP_PKTINFO		0x40
#define TF_IP_RETOPTS		0x80
#define TF_TCP_NODELAY		0x100
#define TF_TCP_MAXSEG		0x200
#define TF_TCP_KEEPALIVE	0x400
#define TF_UDP_CHECKSUM		0x800

#define TM_OPT_SENDMSG	(	TF_IP_OPTIONS	\
			|	TF_IP_TOS	\
			|	TF_IP_TTL	\
			|	TF_IP_DONTROUTE	\
			|	TF_IP_PKTINFO	\
			)

#define _T_CHECK		1
#define _T_NEGOTIATE		2
#define _T_DEFAULT		3
#define _T_CURRENT		4
#define TF_CHECK		(1<<_T_CHECK)
#define TF_NEGOTIATE		(1<<_T_NEGOTIATE)
#define TF_DEFAULT		(1<<_T_DEFAULT)
#define TF_CURRENT		(1<<_T_CURRENT)

STATIC inet_t *inet_opens = NULL;
STATIC inet_t *inet_dflt_dest = NULL;
STATIC inet_t *inet_dflt_lstn = NULL;

/* 
 *  =========================================================================
 *
 *  Socket Callbacks
 *
 *  =========================================================================
 */
STATIC void inet_sk_state_change(struct sock *sk);
STATIC void inet_sk_writeable(struct sock *sk);
STATIC void inet_sk_errors(struct sock *sk);
STATIC void inet_sk_readable(struct sock *sk, int len);

STATIC INLINE void my_inet_socket_put(struct socket *sock, char *f, int l)
{
	if (sock) {
		if (sock->sk)
			sock->sk->user_data = NULL;
		else {
			swerr();
			printd(("SWERR: bad socket put called from %s +%d\n", f, l));
		}
		sock_release(sock);
		return;
	}
	swerr();
	printd(("SWERR: bad socket put called from %s +%d\n", f, l));
}
STATIC INLINE void my_inet_socket_get(struct socket *sock, inet_t * ip, char *f, int l)
{
	struct sock *sk;

	if (sock) {
		if ((sk = sock->sk)) {
			sk->user_data = ip;
			sk->state_change = inet_sk_state_change;
			sk->data_ready = inet_sk_readable;
			sk->write_space = inet_sk_writeable;
			sk->error_report = inet_sk_errors;
#ifdef LINUX_2_4
			sk->protinfo.af_inet.recverr = 1;
#else
			sk->ip_recverr = 1;
#endif
		} else {
			swerr();
			printd(("SWERR: bad socket get called from %s +%d\n", f, l));
		}
		return;
	}
	swerr();
	printd(("SWERR: bad socket get called from %s +%d\n", f, l));
}

#define inet_socket_put(__sock) my_inet_socket_put(__sock,__FILE__,__LINE__)
#define inet_socket_get(__sock,__ip) my_inet_socket_get(__sock,__ip,__FILE__,__LINE__)

/* 
 *  =========================================================================
 *
 *  Buffer Allocation
 *
 *  =========================================================================
 */

#ifdef _DEBUG
STATIC INLINE void my_freemsg(mblk_t * mp, char *file, int line)
{
	if (mp->b_datap->db_ref) {
		freemsg(mp);
		return;
	}
	printd(("ERROR: double free in %s +%d\n", file, line));
}
STATIC INLINE void my_freeb(mblk_t * mp, char *file, int line)
{
	if (mp->b_datap->db_ref) {
		freeb(mp);
		return;
	}
	printd(("ERROR: double free in %s +%d\n", file, line));
}

#undef freemsg
#undef freeb
#define freemsg(__mp) my_freemsg((__mp), __FILE__, __LINE__)
#define freeb(__mp) my_freeb((__mp), __FILE__, __LINE__)
#endif
/* 
 *  BUFSRV calls service routine
 *  -------------------------------------------------------------------------
 */
STATIC void inet_bufsrv(long data)
{
	queue_t *q = (queue_t *) data;

	if (q) {
		inet_t *ip = INET_PRIV(q);

		if (q == ip->rq)
			if (ip->rbid)
				ip->rbid = 0;
		if (q == ip->wq)
			if (ip->wbid)
				ip->wbid = 0;
		qenable(q);
	}
}

/* 
 *  BUFCALL for enobufs
 *  -------------------------------------------------------------------------
 *  Generate a buffer call against a queue.
 */
STATIC INLINE void inet_bufcall(queue_t * q, size_t size, int prior)
{
	if (q) {
		inet_t *ip = INET_PRIV(q);

		if (q == ip->rq)
			if (!ip->rbid)
				ip->rbid = bufcall(size, prior, &inet_bufsrv, (long)q);
		if (q == ip->rq)
			if (!ip->wbid)
				ip->wbid = bufcall(size, prior, &inet_bufsrv, (long)q);
	}
	return;
}

/* 
 *  ALLOCB
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *inet_allocb(queue_t * q, size_t size, int prior)
{
	mblk_t *mp;

	if ((mp = allocb(size, prior)))
		return (mp);
	inet_bufcall(q, size, prior);
	return (NULL);
}

/* 
 *  ESBALLOC
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *inet_esballoc(queue_t * q, unsigned char *base, size_t size, int prior,
				    frtn_t * frtn, int *errp)
{
	mblk_t *mp;

	if ((mp = esballoc(base, size, prior, frtn)))
		return (mp);
	inet_bufcall(q, size, prior);
	return (NULL);
}

/* 
 *  =========================================================================
 *
 *  OPTION Handling
 *
 *  =========================================================================
 */
#define _T_ALIGN_SIZEOF(s) \
	((sizeof((s)) + _T_ALIGN_SIZE - 1) & ~(_T_ALIGN_SIZE - 1))
STATIC INLINE size_t inet_opts_size(inet_opts_t * ops)
{
	size_t len = 0;
	const size_t hlen = sizeof(struct t_opthdr);

	if (ops) {
		if (ops->opt)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->opt));
		if (ops->tos)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->tos));
		if (ops->ttl)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->ttl));
		if (ops->reuse)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->reuse));
		if (ops->norte)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->norte));
		if (ops->bcast)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->bcast));
		if (ops->nodly)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->nodly));
		if (ops->mss)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->mss));
		if (ops->alive)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->alive));
		if (ops->csum)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->csum));
	}
	return (len);
}
STATIC INLINE void inet_build_opts(inet_opts_t * ops, unsigned char **p)
{
	struct t_opthdr *oh;
	const size_t hlen = sizeof(struct t_opthdr);
	const size_t olen = hlen + sizeof(t_scalar_t);

	if (!ops || ops->opt) {
		oh = ((typeof(oh)) * p)++;
		oh->len = hlen + sizeof(struct ip_options);
		oh->level = T_INET_IP;
		oh->name = T_IP_OPTIONS;
		oh->status = (!ops || ops->flags & TF_IP_OPTIONS) ? T_SUCCESS : T_FAILURE;
		*((struct ip_options *)*p) = *(ops->opt);
		*p += _T_ALIGN_SIZEOF(*ops->opt);
	}
	if (!ops || ops->tos) {
		oh = ((typeof(oh)) * p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_TOS;
		oh->status = (!ops || ops->flags & TF_IP_TOS) ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p) = *(ops->tos);
		*p += _T_ALIGN_SIZEOF(*ops->tos);
	}
	if (!ops || ops->ttl) {
		oh = ((typeof(oh)) * p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_TTL;
		oh->status = (!ops || ops->flags & TF_IP_TTL) ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p) = *(ops->ttl);
		*p += _T_ALIGN_SIZEOF(*ops->ttl);
	}
	if (!ops || ops->reuse) {
		oh = ((typeof(oh)) * p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_REUSEADDR;
		oh->status = (!ops || ops->flags & TF_IP_REUSEADDR) ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p) = *(ops->reuse);
		*p += _T_ALIGN_SIZEOF(*ops->reuse);
	}
	if (!ops || ops->norte) {
		oh = ((typeof(oh)) * p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_DONTROUTE;
		oh->status = (!ops || ops->flags & TF_IP_DONTROUTE) ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p) = *(ops->norte);
		*p += _T_ALIGN_SIZEOF(*ops->norte);
	}
	if (!ops || ops->bcast) {
		oh = ((typeof(oh)) * p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_BROADCAST;
		oh->status = (!ops || ops->flags & TF_IP_BROADCAST) ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p) = *(ops->bcast);
		*p += _T_ALIGN_SIZEOF(*ops->bcast);
	}
	if (!ops || ops->nodly) {
		oh = ((typeof(oh)) * p)++;
		oh->len = olen;
		oh->level = T_INET_TCP;
		oh->name = T_TCP_NODELAY;
		oh->status = (!ops || ops->flags & TF_TCP_NODELAY) ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p) = *(ops->nodly);
		*p += _T_ALIGN_SIZEOF(*ops->nodly);
	}
	if (!ops || ops->mss) {
		oh = ((typeof(oh)) * p)++;
		oh->len = olen;
		oh->level = T_INET_TCP;
		oh->name = T_TCP_MAXSEG;
		oh->status = (!ops || ops->flags & TF_TCP_MAXSEG) ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p) = *(ops->mss);
		*p += _T_ALIGN_SIZEOF(*ops->mss);
	}
	if (!ops || ops->alive) {
		oh = ((typeof(oh)) * p)++;
		oh->len = olen;
		oh->level = T_INET_TCP;
		oh->name = T_TCP_KEEPALIVE;
		oh->status = (!ops || ops->flags & TF_TCP_KEEPALIVE) ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p) = *(ops->alive);
		*p += _T_ALIGN_SIZEOF(*ops->alive);
	}
	if (!ops || ops->csum) {
		oh = ((typeof(oh)) * p)++;
		oh->len = olen;
		oh->level = T_INET_UDP;
		oh->name = T_UDP_CHECKSUM;
		oh->status = (!ops || ops->flags & TF_UDP_CHECKSUM) ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p) = *(ops->csum);
		*p += _T_ALIGN_SIZEOF(*ops->csum);
	}
}
STATIC INLINE int inet_parse_opts(inet_t * ip, inet_opts_t * ops, unsigned char *op,
				  size_t len)
{
	struct t_opthdr *oh;

	for (oh = _T_OPT_FIRSTHDR_OFS(op, len, 0); oh; oh = _T_OPT_NEXTHDR_OFS(op, len, oh, 0)) {
		switch (oh->level) {
		case T_INET_IP:
			switch (oh->name) {
			case T_IP_OPTIONS:
				ops->opt = (void *)_T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_IP_OPTIONS;
				continue;
			case T_IP_TOS:
				ops->tos = (void *)_T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_IP_TOS;
				continue;
			case T_IP_TTL:
				ops->ttl = (void *)_T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_IP_TTL;
				continue;
			case T_IP_REUSEADDR:
				ops->reuse = (void *)_T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_IP_REUSEADDR;
				continue;
			case T_IP_DONTROUTE:
				ops->norte = (void *)_T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_IP_DONTROUTE;
				continue;
			case T_IP_BROADCAST:
				ops->bcast = (void *)_T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_IP_BROADCAST;
				continue;
			}
			break;
		case T_INET_TCP:
			if (ip->proto == T_INET_TCP)
				switch (oh->name) {
				case T_TCP_NODELAY:
					ops->nodly = (void *)_T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_TCP_NODELAY;
					continue;
				case T_TCP_MAXSEG:
					ops->mss = (void *)_T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_TCP_MAXSEG;
					continue;
				case T_TCP_KEEPALIVE:
					ops->alive = (void *)_T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_TCP_KEEPALIVE;
					continue;
				}
			break;
		case T_INET_UDP:
			if (ip->proto == T_INET_UDP)
				switch (oh->name) {
				case T_UDP_CHECKSUM:
					ops->csum = (void *)_T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_UDP_CHECKSUM;
					continue;
				}
			break;
		case T_INET_SCTP:
			/* skip these for now */
			break;
		}
	}
	if (oh)
		return (TBADOPT);
	return (0);
}
STATIC INLINE size_t inet_cmsg_size(inet_opts_t * ops, int flags)
{
	size_t len = 0;
	const size_t hlen = CMSG_ALIGN(sizeof(struct cmsghdr));

	if (ops) {
		if (ops->opt && flags & TF_IP_OPTIONS)
			len += hlen + CMSG_ALIGN(sizeof(*(ops->opt)));
		if (ops->tos && flags & TF_IP_TOS)
			len += hlen + CMSG_ALIGN(sizeof(*(ops->tos)));
		if (ops->ttl && flags & TF_IP_TTL)
			len += hlen + CMSG_ALIGN(sizeof(*(ops->ttl)));
		if (ops->reuse && flags & TF_IP_REUSEADDR)
			len += hlen + CMSG_ALIGN(sizeof(*(ops->reuse)));
		if (ops->norte && flags & TF_IP_DONTROUTE)
			len += hlen + CMSG_ALIGN(sizeof(*(ops->norte)));
		if (ops->bcast && flags & TF_IP_BROADCAST)
			len += hlen + CMSG_ALIGN(sizeof(*(ops->bcast)));
		if (ops->nodly && flags & TF_TCP_NODELAY)
			len += hlen + CMSG_ALIGN(sizeof(*(ops->nodly)));
		if (ops->mss && flags & TF_TCP_MAXSEG)
			len += hlen + CMSG_ALIGN(sizeof(*(ops->mss)));
		if (ops->alive && flags & TF_TCP_KEEPALIVE)
			len += hlen + CMSG_ALIGN(sizeof(*(ops->alive)));
#ifdef LINUX_2_4
#endif
		if (ops->csum && flags & TF_UDP_CHECKSUM)
			len += hlen + CMSG_ALIGN(sizeof(*(ops->csum)));
#ifdef CONFIG_SCTP
#endif
	}
	return (len);
}
STATIC INLINE void inet_build_cmsg(inet_opts_t * ops, struct msghdr *msg, int flags)
{
	struct cmsghdr *ch;
	unsigned char *p = msg->msg_control;
	const size_t hlen = CMSG_ALIGN(sizeof(struct cmsghdr));

	if (!ops || (ops->opt && flags & TF_IP_OPTIONS)) {
		ch = ((typeof(ch)) p)++;
		ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->opt));
		ch->cmsg_level = SOL_IP;
		ch->cmsg_type = IP_OPTIONS;
		*((typeof(ops->opt)) p) = *(ops->opt);
		p += CMSG_ALIGN(sizeof(*ops->opt));
	}
	if (!ops || (ops->tos && flags & TF_IP_TOS)) {
		ch = ((typeof(ch)) p)++;
		ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->tos));
		ch->cmsg_level = SOL_IP;
		ch->cmsg_type = IP_TOS;
		*((typeof(ops->tos)) p) = *(ops->tos);
		p += CMSG_ALIGN(sizeof(*ops->tos));
	}
	if (!ops || (ops->ttl && flags & TF_IP_TTL)) {
		ch = ((typeof(ch)) p)++;
		ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->ttl));
		ch->cmsg_level = SOL_IP;
		ch->cmsg_type = IP_TTL;
		*((typeof(ops->ttl)) p) = *(ops->ttl);
		p += CMSG_ALIGN(sizeof(*ops->ttl));
	}
	if (!ops || (ops->reuse && flags & TF_IP_REUSEADDR)) {
		ch = ((typeof(ch)) p)++;
		ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->reuse));
		ch->cmsg_level = SOL_SOCKET;
		ch->cmsg_type = SO_REUSEADDR;
		*((typeof(ops->reuse)) p) = *(ops->reuse);
		p += CMSG_ALIGN(sizeof(*ops->reuse));
	}
	if (!ops || (ops->norte && flags & TF_IP_DONTROUTE)) {
		ch = ((typeof(ch)) p)++;
		ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->norte));
		ch->cmsg_level = SOL_SOCKET;
		ch->cmsg_type = SO_DONTROUTE;
		*((typeof(ops->norte)) p) = *(ops->norte);
		p += CMSG_ALIGN(sizeof(*ops->norte));
	}
	if (!ops || (ops->bcast && flags & TF_IP_BROADCAST)) {
		ch = ((typeof(ch)) p)++;
		ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->bcast));
		ch->cmsg_level = SOL_SOCKET;
		ch->cmsg_type = SO_BROADCAST;
		*((typeof(ops->bcast)) p) = *(ops->bcast);
		p += CMSG_ALIGN(sizeof(*ops->bcast));
	}
	if (!ops || (ops->nodly && flags & TF_TCP_NODELAY)) {
		ch = ((typeof(ch)) p)++;
		ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->nodly));
		ch->cmsg_level = SOL_TCP;
		ch->cmsg_type = TCP_NODELAY;
		*((typeof(ops->nodly)) p) = *(ops->nodly);
		p += CMSG_ALIGN(sizeof(*ops->nodly));
	}
	if (!ops || (ops->mss && flags & TF_TCP_MAXSEG)) {
		ch = ((typeof(ch)) p)++;
		ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->mss));
		ch->cmsg_level = SOL_TCP;
		ch->cmsg_type = TCP_MAXSEG;
		*((typeof(ops->mss)) p) = *(ops->mss);
		p += CMSG_ALIGN(sizeof(*ops->mss));
	}
	if (!ops || (ops->alive && flags & TF_TCP_KEEPALIVE)) {
		ch = ((typeof(ch)) p)++;
		ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->alive));
		ch->cmsg_level = SOL_SOCKET;
		ch->cmsg_type = SO_KEEPALIVE;
		*((typeof(ops->alive)) p) = *(ops->alive);
		p += CMSG_ALIGN(sizeof(*ops->alive));
	}
#ifdef UDP_CHECKSUM
	if (!ops || (ops->csum && flags & TF_UDP_CHECKSUM)) {
		ch = ((typeof(ch)) p)++;
		ch->cmsg_len = hlen + CMSG_ALIGN(sizeof(*ops->csum));
		ch->cmsg_level = SOL_UDP;
		ch->cmsg_type = UDP_CHECKSUM;
		*((typeof(ops->csum)) p) = *(ops->csum);
		p += CMSG_ALIGN(sizeof(*ops->csum));
	}
#endif
}
STATIC INLINE int inet_parse_cmsg(struct msghdr *msg, inet_opts_t * opts)
{
	struct cmsghdr *cmsg;

	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
		if (cmsg->cmsg_len < sizeof(struct cmsghdr)
		    || (unsigned long)(((char *)cmsg - (char *)msg->msg_control) +
				       cmsg->cmsg_len) > msg->msg_controllen) {
			return -EINVAL;
		}
		switch (cmsg->cmsg_level) {
		case SOL_IP:
			switch (cmsg->cmsg_type) {
			case IP_OPTIONS:
				if (cmsg->cmsg_len <= 40) {
					opts->opt = (typeof(opts->opt))
					    CMSG_DATA(cmsg);
					opts->flags |= TF_IP_OPTIONS;
				}
				break;
			case IP_TTL:
				if (cmsg->cmsg_len == sizeof(*opts->ttl)) {
					opts->ttl = (typeof(opts->ttl))
					    CMSG_DATA(cmsg);
					opts->flags |= TF_IP_TTL;
				}
				break;
			case IP_TOS:
				if (cmsg->cmsg_len == sizeof(*opts->tos)) {
					opts->tos = (typeof(opts->tos))
					    CMSG_DATA(cmsg);
					opts->flags |= TF_IP_TOS;
				}
				break;
			case IP_RETOPTS:
				if (cmsg->cmsg_len == sizeof(*opts->ropt)) {
					opts->ropt = (typeof(opts->ropt))
					    CMSG_DATA(cmsg);
					opts->flags |= TF_IP_RETOPTS;
				}
				break;
			case IP_PKTINFO:
				if (cmsg->cmsg_len == sizeof(*opts->pkinfo)) {
					opts->pkinfo = (typeof(opts->pkinfo))
					    CMSG_DATA(cmsg);
					opts->flags |= TF_IP_PKTINFO;
				}
				break;
			case IP_HDRINCL:
			case IP_ROUTER_ALERT:
			case IP_RECVOPTS:
			case IP_PKTOPTIONS:
			case IP_MTU_DISCOVER:
			case IP_RECVERR:
			case IP_RECVTTL:
			case IP_RECVTOS:
			case IP_MTU:
				break;
			}
			break;
		case SOL_TCP:
			switch (cmsg->cmsg_type) {
			case TCP_NODELAY:
			case TCP_MAXSEG:
			case TCP_CORK:
#ifdef LINUX_2_4
			case TCP_KEEPIDLE:
			case TCP_KEEPINTVL:
			case TCP_KEEPCNT:
			case TCP_SYNCNT:
			case TCP_LINGER2:
			case TCP_DEFER_ACCEPT:
			case TCP_WINDOW_CLAMP:
			case TCP_INFO:
			case TCP_QUICKACK:
#endif
				break;
			}
			break;
		case SOL_UDP:
			switch (cmsg->cmsg_type) {
			}
			break;
#ifdef CONFIG_SCTP
		case SOL_SCTP:
			switch (cmsg->cmsg_type) {
			case SCTP_NODELAY:
			case SCTP_MAXSEG:
			case SCTP_CORK:
			case SCTP_RECVSID:
			case SCTP_RECVPPI:
			case SCTP_RECVHDR:
			case SCTP_SID:
			case SCTP_PPI:
			case SCTP_HB:
			case SCTP_RTO:
			case SCTP_HDR:
				break;
			}
			break;
#endif
		default:
			break;
		}
	}
	return (0);
}

/* 
 *  =========================================================================
 *
 *  OPTIONS Handling
 *
 *  =========================================================================
 */
STATIC inet_dflt_t opt_defaults = {
	ip_default_opt,
	ip_default_tos,
	ip_default_ttl,
	ip_default_reuse,
	ip_default_norte,
	ip_default_bcast,
	tcp_default_nodly,
	tcp_default_mss,
	tcp_default_alive,
	udp_default_csum
};

/* 
 *  CHECK Options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC INLINE int inet_opt_check(inet_t * ip, inet_opts_t * opt)
{
	if (opt->flags) {
		opt->flags = 0;
		if (opt->opt)
			opt->flags |= TF_IP_OPTIONS;
		if (opt->tos && !(*(opt->tos) & 0x1e))
			opt->flags |= TF_IP_TOS;
		if (opt->ttl && !(*(opt->ttl) & 0xff))
			opt->flags |= TF_IP_TTL;
		if (opt->reuse)
			opt->flags |= TF_IP_REUSEADDR;
		if (opt->norte)
			opt->flags |= TF_IP_DONTROUTE;
		if (opt->bcast)
			opt->flags |= TF_IP_BROADCAST;
		if (ip->proto == T_INET_TCP) {
			if (opt->nodly)
				opt->flags |= TF_TCP_NODELAY;
			if (opt->mss)
				opt->flags |= TF_TCP_MAXSEG;
			if (opt->alive)
				opt->flags |= TF_TCP_KEEPALIVE;
		}
		if (ip->proto == T_INET_UDP) {
			if (opt->csum)
				opt->flags |= TF_UDP_CHECKSUM;
		}
	}
	return (0);
}

/* 
 *  DEFAULT Options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC INLINE int inet_opt_default(inet_t * ip, inet_opts_t * opt)
{
	int flags = opt->flags;

	opt->flags = 0;
	if (!flags || opt->tos) {
		opt->tos = &opt_defaults.tos;
		opt->flags |= TF_IP_TOS;
	}
	if (!flags || opt->ttl) {
		opt->ttl = &opt_defaults.ttl;
		opt->flags |= TF_IP_TTL;
	}
	if (!flags || opt->reuse) {
		opt->reuse = &opt_defaults.reuse;
		opt->flags |= TF_IP_REUSEADDR;
	}
	if (!flags || opt->norte) {
		opt->norte = &opt_defaults.norte;
		opt->flags |= TF_IP_DONTROUTE;
	}
	if (!flags || opt->bcast) {
		opt->bcast = &opt_defaults.bcast;
		opt->flags |= TF_IP_BROADCAST;
	}
	if (ip->proto == T_INET_TCP) {
		if (!flags || opt->nodly) {
			opt->nodly = &opt_defaults.nodly;
			opt->flags |= TF_TCP_NODELAY;
		}
		if (!flags || opt->mss) {
			opt->mss = &opt_defaults.mss;
			opt->flags |= TF_TCP_MAXSEG;
		}
		if (!flags || opt->alive) {
			opt->alive = &opt_defaults.alive;
			opt->flags |= TF_TCP_KEEPALIVE;
		}
	}
	if (ip->proto == T_INET_UDP) {
		if (!flags || opt->csum) {
			opt->csum = &opt_defaults.csum;
			opt->flags |= TF_UDP_CHECKSUM;
		}
	}
	return (0);
}

/* 
 *  NEGOTIATE Options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC INLINE int inet_opt_negotiate(inet_t * ip, inet_opts_t * opt)
{
	struct socket *sock;
	struct sock *sk;

	ensure(ip, return (-EFAULT));
	if ((sock = ip->sock))
		sk = sock->sk;
	else
		sk = NULL;
	opt->flags = 0;
	if (opt->opt) {
		ip->options.opt = *(opt->opt);
#ifdef LINUX_2_4
		if (sock)
			sk->protinfo.af_inet.opt = &ip->options.opt;
#else
		if (sock)
			sk->opt = &ip->options.opt;
#endif
		opt->flags |= TF_IP_OPTIONS;
	}
	if (opt->tos) {
		ip->options.tos = *(opt->tos);
#ifdef LINUX_2_4
		if (sock)
			sk->protinfo.af_inet.tos = ip->options.tos;
#else
		if (sock)
			sk->ip_tos = ip->options.tos;
#endif
		opt->flags |= TF_IP_TOS;
	}
	if (opt->ttl) {
		ip->options.ttl = *(opt->ttl);
#ifdef LINUX_2_4
		if (sock)
			sk->protinfo.af_inet.ttl = ip->options.ttl;
#else
		if (sock)
			sk->ip_ttl = ip->options.ttl;
#endif
		opt->flags |= TF_IP_TTL;
	}
	if (opt->reuse) {
		ip->options.reuse = *(opt->reuse) ? 1 : 0;
		if (sock)
			sk->reuse = ip->options.reuse;
		opt->flags |= TF_IP_REUSEADDR;
	}
	if (opt->norte) {
		ip->options.norte = *(opt->norte) ? 1 : 0;
		if (sock)
			sk->localroute = ip->options.norte;
		opt->flags |= TF_IP_DONTROUTE;
	}
	if (opt->bcast) {
		ip->options.bcast = *(opt->bcast) ? 1 : 0;
		if (sock)
			sk->broadcast = ip->options.bcast;
		opt->flags |= TF_IP_BROADCAST;
	}
	if (opt->nodly) {
		ip->options.nodly = *(opt->nodly) ? 1 : 0;
#ifdef LINUX_2_4
		if (sock)
			sk->tp_pinfo.af_tcp.nonagle = ip->options.nodly;
#else
		if (sock)
			sk->nonagle = ip->options.nodly;
#endif
		opt->flags |= TF_TCP_NODELAY;
	}
	if (opt->mss) {
		ip->options.mss = *(opt->mss) ? 1 : 0;
		if (sock)
			sk->tp_pinfo.af_tcp.user_mss = ip->options.mss;
		opt->flags |= TF_TCP_MAXSEG;
	}
	if (opt->alive) {
		ip->options.alive = *(opt->alive) ? 1 : 0;
		if (sock)
			sk->keepopen = ip->options.alive;
		opt->flags |= TF_TCP_KEEPALIVE;
	}
	if (opt->csum) {
		ip->options.csum = *(opt->csum) ? 1 : 0;
		// if ( sock ) sk->csum = ip->options.csum;
		opt->flags |= TF_UDP_CHECKSUM;
	}
	return (0);
}

/* 
 *  CURRENT Options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC INLINE int inet_opt_current(inet_t * ip, inet_opts_t * opt)
{
	uint flags = opt->flags;

	opt->flags = 0;
	if (!flags || opt->opt) {
		opt->opt = &ip->options.opt;
		opt->flags |= TF_IP_OPTIONS;
	}
	if (!flags || opt->tos) {
		opt->tos = &ip->options.tos;
		opt->flags |= TF_IP_TOS;
	}
	if (!flags || opt->ttl) {
		opt->ttl = &ip->options.ttl;
		opt->flags |= TF_IP_TTL;
	}
	if (!flags || opt->reuse) {
		opt->reuse = &ip->options.reuse;
		opt->flags |= TF_IP_REUSEADDR;
	}
	if (!flags || opt->norte) {
		opt->norte = &ip->options.norte;
		opt->flags |= TF_IP_DONTROUTE;
	}
	if (!flags || opt->bcast) {
		opt->bcast = &ip->options.bcast;
		opt->flags |= TF_IP_BROADCAST;
	}
	if (ip->proto == T_INET_TCP) {
		if (!flags || opt->nodly) {
			opt->nodly = &ip->options.nodly;
			opt->flags |= TF_TCP_NODELAY;
		}
		if (!flags || opt->mss) {
			opt->mss = &ip->options.mss;
			opt->flags |= TF_TCP_MAXSEG;
		}
		if (!flags || opt->alive) {
			opt->alive = &ip->options.alive;
			opt->flags |= TF_TCP_KEEPALIVE;
		}
	}
	if (ip->proto == T_INET_UDP) {
		if (!flags || opt->csum) {
			opt->csum = &ip->options.csum;
			opt->flags |= TF_UDP_CHECKSUM;
		}
	}
	return (0);
}

/* 
 *  =========================================================================
 *
 *  IP T-Provider --> T-User Primitives (Indication, Confirmation and Ack)
 *
 *  =========================================================================
 */
/* 
 *  T_CONN_IND          11 - Connection Indication
 *  ---------------------------------------------------------------
 */
STATIC INLINE int t_conn_ind(queue_t * q, inet_addr_t * src, inet_opts_t * opts, mblk_t * cp,
			     mblk_t * dp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_conn_ind *p;
	size_t src_len = src ? sizeof(*src) : 0;
	size_t opt_len = inet_opts_size(opts);
	size_t msg_len = sizeof(*p) + src_len + opt_len;

	printd(("%p T_CONN_IND\n", ip));
	ensure(((1 << ip->state) & TSM_LISTEN), return (-EFAULT));
	if (bufq_length(&ip->conq) > ip->conind)
		goto erestart;
	if (!canputnext(ip->rq))
		goto ebusy;
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_CONN_IND;
	p->SRC_length = src_len;
	p->SRC_offset = src_len ? sizeof(*p) : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + src_len : 0;
	p->SEQ_number = (ulong) cp;
	if (src_len) {
		bcopy(src, mp->b_wptr, src_len);
		mp->b_wptr += src_len;
	}
	if (opt_len) {
		inet_build_opts(opts, &mp->b_wptr);
	}
	bufq_queue(&ip->conq, cp);
	ip->state = TS_WRES_CIND;
	putnext(ip->rq, mp);
	return (QR_ABSORBED);	/* absorbed cp */
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	return (err);
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	return (err);
      erestart:
	err = -ERESTART;
	ptrace(("ERROR: too many conn inds\n"));
	return (err);
}

/* 
 *  T_CONN_CON          12 - Connection Confirmation
 *  ---------------------------------------------------------------
 */
STATIC int t_conn_con(queue_t * q, inet_addr_t * res, inet_opts_t * opts, mblk_t * dp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_conn_con *p;
	size_t res_len = res ? sizeof(*res) : 0;
	size_t opt_len = inet_opts_size(opts);
	size_t msg_len = sizeof(*p) + res_len + opt_len;

	printd(("%p T_CONN_CON\n", ip));
	ensure((ip->state == TS_WCON_CREQ), return (-EFAULT));
	/* this shouldn't happen, we probably shouldn't even check */
	if (!canputnext(ip->rq))
		goto ebusy;
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	mp->b_band = 1;		/* expedite */
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_CONN_CON;
	p->RES_length = res_len;
	p->RES_offset = res_len ? sizeof(*p) : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + res_len : 0;
	if (res_len) {
		bcopy(res, mp->b_wptr, res_len);
		mp->b_wptr += res_len;
	}
	if (opt_len) {
		inet_build_opts(opts, &mp->b_wptr);
	}
	ip->state = TS_DATA_XFER;
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	return (err);
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	return (err);
}

/* 
 *  T_DISCON_IND        13 - Disconnect Indication
 *  ---------------------------------------------------------------
 */
STATIC INLINE mblk_t *t_seq_find(inet_t * ip, mblk_t * rp)
{
	mblk_t *mp;
	struct sock *sk;

	if (!rp)
		return (rp);
	sk = *((struct sock **)rp->b_rptr);
	for (mp = bufq_head(&ip->conq); mp && *((struct sock **)mp->b_rptr) != sk;
	     mp = mp->b_next) ;
	return (mp);
}
STATIC INLINE void t_seq_release(inet_t * ip, mblk_t * rp)
{
	struct sock *sk;

	if (!rp) {
		swerr();
		return;
	}
	sk = *((struct sock **)rp->b_rptr);
	bufq_unlink(&ip->conq, rp);
	freemsg(rp);
	inet_socket_put(sk->socket);
	return;
}
STATIC INLINE ulong t_seq_delete(inet_t * ip, mblk_t * rp)
{
	mblk_t *mp;

	if ((mp = t_seq_find(ip, rp))) {
		t_seq_release(ip, rp);
		return ((ulong) mp);
	}
	return (0);
}
STATIC int t_discon_ind(queue_t * q, inet_addr_t * res, uint orig, uint reason, mblk_t * cp,
			mblk_t * dp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_discon_ind *p;
	size_t msg_len = sizeof(*p);
	ulong seq = 0;

	printd(("%p T_DISCON_IND\n", ip));
	(void)res;
	ensure(((1 << ip->state) & TSM_CONNECTED), return (-EFAULT));
	if (!canputnext(ip->rq))
		goto ebusy;
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	if (cp && !(seq = t_seq_delete(ip, cp)))
		goto badseq;
	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_DISCON_IND;
	p->DISCON_reason = reason;
	p->SEQ_number = seq;
	if (!bufq_length(&ip->conq))
		ip->state = TS_IDLE;
	else
		ip->state = TS_WRES_CIND;
	mp->b_cont = dp;
	putnext(ip->rq, mp);
	return (0);
      badseq:
	freemsg(mp);
	err = -EFAULT;
	ptrace(("ERROR: bad sequence number\n"));
	return (err);
      enobufs:
	fixme(("We must do this before pulling from socket\n"));
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	return (err);
      ebusy:
	fixme(("We must do this before pulling from socket\n"));
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	return (err);
}

/* 
 *  T_DATA_IND          14 - Data Indication
 *  ---------------------------------------------------------------
 */
STATIC INLINE int t_data_ind(queue_t * q, uint flags, mblk_t * dp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_data_ind *p;
	size_t msg_len = sizeof(*p);

	printd(("%p T_DATA_IND\n", ip));
	ensure(((1 << ip->state) & TSM_INDATA), return (-EFAULT));
	if (!canputnext(ip->rq))
		goto ebusy;
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_DATA_IND;
	p->MORE_flag = flags;
	mp->b_cont = dp;
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	fixme(("We must do this before pulling from socket\n"));
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	return (err);
      ebusy:
	fixme(("We must do this before pulling from socket\n"));
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	return (err);
}

/* 
 *  T_EXDATA_IND        15 - Expedited Data Indication
 *  ---------------------------------------------------------------
 */
STATIC INLINE int t_exdata_ind(queue_t * q, uint flags, mblk_t * dp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_exdata_ind *p;
	size_t msg_len = sizeof(*p);

	printd(("%p T_EXDATA_IND\n", ip));
	ensure(((1 << ip->state) & TSM_INDATA), return (-EFAULT));
	if (!canputnext(ip->rq))
		goto ebusy;
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	mp->b_band = 1;		/* expedite */
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_EXDATA_IND;
	p->MORE_flag = flags;
	mp->b_cont = dp;
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	fixme(("We must do this before pulling from socket\n"));
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	return (err);
      ebusy:
	fixme(("We must do this before pulling from socket\n"));
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	return (err);
}

/* 
 *  T_INFO_ACK          16 - Information acknowledgement
 *  ---------------------------------------------------------------
 */
STATIC INLINE int t_info_ack(queue_t * q)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_info_ack *p;
	size_t msg_len = sizeof(*p);

	printd(("%p T_INFO_ACK\n", ip));
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_INFO_ACK;
	p->TSDU_size = ip->tsdu;
	p->ETSDU_size = ip->etsdu;
	p->CDATA_size = ip->cdata;
	p->DDATA_size = ip->ddata;
	p->ADDR_size = ip->asize;
	p->OPT_size = ip->osize;
	p->TIDU_size = ip->tidu;
	p->SERV_type = ip->serv_type;
	p->CURRENT_state = ip->state;
	p->PROVIDER_flag = ip->prov_flags;
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (err);
}

/* 
 *  T_BIND_ACK          17 - Bind Acknowledgement
 *  ---------------------------------------------------------------
 */
STATIC INLINE int t_bind_ack(queue_t * q, inet_addr_t * add)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_bind_ack *p;
	size_t add_len = add ? sizeof(*add) : 0;
	size_t msg_len = sizeof(*p) + add_len;

	printd(("%p T_BIND_ACK\n", ip));
	ensure(ip->state == TS_WACK_BREQ, return (-EFAULT));
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_BIND_ACK;
	p->ADDR_length = add_len;
	p->ADDR_offset = add_len ? sizeof(*p) : 0;
	p->CONIND_number = 0;
	if (add_len) {
		bcopy(add, mp->b_wptr, add_len);
		mp->b_wptr++;
	}
	ip->state = TS_IDLE;
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (err);
}

/* 
 *  T_ERROR_ACK         18 - Error Acknowledgement
 *  -------------------------------------------------------------------------
 */
STATIC int t_error_ack(queue_t * q, ulong prim, long error)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_error_ack *p;
	size_t msg_len = sizeof(*p);

	printd(("%p T_ERROR_ACK\n", ip));
	switch (error) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (error);
	case 0:
		never();
		return (error);
	}
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_ERROR_ACK;
	p->ERROR_prim = prim;
	p->TLI_error = error < 0 ? TSYSERR : error;
	p->UNIX_error = error < 0 ? -error : 0;
	/*
	 *  This is to only try and get the state correct for putnext.
	 */
	if (error != TOUTSTATE) {
		switch (ip->state) {
#ifdef TS_WACK_OPTREQ
		case TS_WACK_OPTREQ:
			ensure(prim == T_OPTMGMT_REQ, break);
			ip->state = TS_IDLE;
			break;
#endif
		case TS_WACK_UREQ:
			ensure(prim == T_UNBIND_REQ, break);
			ip->state = TS_IDLE;
			break;
		case TS_WACK_CREQ:
			ensure(prim == T_CONN_REQ, break);
			ip->state = TS_IDLE;
			break;
		case TS_WACK_BREQ:
			ensure(prim == T_BIND_REQ, break);
			ip->state = TS_UNBND;
			break;
		case TS_WACK_CRES:
			ensure(prim == T_CONN_RES, break);
			ip->state = TS_WRES_CIND;
			break;
		case TS_WACK_DREQ6:
			ensure(prim == T_DISCON_REQ, break);
			ip->state = TS_WCON_CREQ;
			break;
		case TS_WACK_DREQ7:
			ensure(prim == T_DISCON_REQ, break);
			ip->state = TS_WRES_CIND;
			break;
		case TS_WACK_DREQ9:
			ensure(prim == T_DISCON_REQ, break);
			ip->state = TS_DATA_XFER;
			break;
		case TS_WACK_DREQ10:
			ensure(prim == T_DISCON_REQ, break);
			ip->state = TS_WIND_ORDREL;
			break;
		case TS_WACK_DREQ11:
			ensure(prim == T_DISCON_REQ, break);
			ip->state = TS_WREQ_ORDREL;
			break;
			/* 
			 *  Note: if we are not in a WACK state we simply do
			 *  not change state.  This occurs normally when we
			 *  are responding to a T_OPTMGMT_REQ in other than
			 *  TS_IDLE state.
			 */
		}
	}
	putnext(ip->rq, mp);
	/*
	 *  Returning -EPROTO here will ensure that the old state is restored
	 *  correctly.  If we return QR_DONE, then the state will never be
	 *  restored.
	 */
	return (-EPROTO);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (err);
}

/* 
 *  T_OK_ACK            19 - Success Acknowledgement
 *  -------------------------------------------------------------------------
 */
STATIC int t_ok_ack(queue_t * q, ulong prim, ulong seq, ulong tok)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_ok_ack *p;
	const size_t msg_len = sizeof(*p);

	printd(("%p T_OK_ACK\n", ip));
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_OK_ACK;
	p->CORRECT_prim = prim;
	switch (ip->state) {
	case TS_WACK_CREQ:
		ip->state = TS_WCON_CREQ;
		break;
	case TS_WACK_UREQ:
		ip->state = TS_UNBND;
		break;
	case TS_WACK_CRES:
		if (tok && seq) {
			queue_t *aq = (queue_t *) tok;
			inet_t *ap = INET_PRIV(aq);
			mblk_t *cp = (mblk_t *) seq;
			struct sock *sk = *((struct sock **)cp->b_rptr);

			ap->sock = sk->socket;
			inet_socket_get(ap->sock, ap);
			bufq_unlink(&ip->conq, cp);
			freemsg(cp);

			ap->state = TS_DATA_XFER;

			if (ap != ip) {
				if (bufq_length(&ip->conq))
					ip->state = TS_WRES_CIND;
				else
					ip->state = TS_IDLE;
				ap->family = ip->family;
				ap->type = ip->type;
				ap->proto = ip->proto;
				ap->src = ip->src;
				ap->options = ip->options;
				bcopy(ip->_pad, ap->_pad, sizeof(ip->_pad));
				ap->serv_type = ip->serv_type;
			}
			ap->dst.sin_family = ap->family;
			ap->dst.sin_port = sk->dport;
			ap->dst.sin_addr.s_addr = sk->daddr;
		}
		break;
	case TS_WACK_DREQ7:
		if (seq) {
			mblk_t *cp = (mblk_t *) seq;
			struct sock *sk = *((struct sock **)cp->b_rptr);

			bufq_unlink(&ip->conq, cp);
			freemsg(cp);
			inet_socket_put(sk->socket);
			if (bufq_length(&ip->conq))
				ip->state = TS_WRES_CIND;
			else
				ip->state = TS_IDLE;
			break;
		}
		swerr();
		break;
	case TS_WACK_DREQ6:
	case TS_WACK_DREQ9:
	case TS_WACK_DREQ10:
	case TS_WACK_DREQ11:
		inet_socket_put(xchg(&ip->sock, NULL));
		ip->state = TS_UNBND;
		if ((err = sock_create(ip->family, ip->type, ip->proto, &ip->sock))) {
			ptrace(("ERROR: couldn't allocate new socket\n"));
			freemsg(mp);
			return t_error_ack(q, prim, err);
		}
		if ((err =
		     ip->sock->ops->bind(ip->sock, (struct sockaddr *)&ip->src,
					 sizeof(ip->src)))) {
			ptrace(("ERROR: couldn't bind new socket\n"));
			inet_socket_put(xchg(&ip->sock, NULL));
			freemsg(mp);
			return t_error_ack(q, prim, err);
		}
		ip->state = TS_IDLE;
		break;
	default:
		break;
		/* 
		 *  Note: if we are not in a WACK state we simply do
		 *  not change state.  This occurs normally when we
		 *  are responding to a T_OPTMGMT_REQ in other than
		 *  the TS_IDLE state.
		 */
	}
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (err);
}

/* 
 *  T_UNIDATA_IND       20 - Unitdata indication
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int t_unitdata_ind(queue_t * q, inet_addr_t * src, inet_opts_t * opts,
				 mblk_t * dp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_unitdata_ind *p;
	size_t src_len = src ? sizeof(*src) : 0;
	size_t opt_len = inet_opts_size(opts);
	size_t msg_len = sizeof(*p) + src_len + opt_len;

	printd(("%p T_UNITDATA_IND\n", ip));
	if (!canputnext(ip->rq))
		goto ebusy;
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_UNITDATA_IND;
	p->SRC_length = src_len;
	p->SRC_offset = src_len ? sizeof(*p) : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + src_len : 0;
	if (src_len) {
		bcopy(src, mp->b_wptr, src_len);
		mp->b_wptr += src_len;
	}
	if (opt_len) {
		inet_build_opts(opts, &mp->b_wptr);
	}
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (err);
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	return (err);
}

/* 
 *  T_UDERROR_IND       21 - Unitdata Error Indication
 *  -------------------------------------------------------------------------
 *  This primitive indicates to the transport user that a datagram with the
 *  specified destination address and options produced an error.
 */
STATIC INLINE int t_uderror_ind(queue_t * q, inet_addr_t * src, inet_addr_t * dst,
				inet_opts_t * opts, mblk_t * dp, int etype)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_uderror_ind *p;
	size_t dst_len = dst ? sizeof(*dst) : 0;
	size_t opt_len = inet_opts_size(opts);
	size_t msg_len = sizeof(*p) + dst_len + opt_len;

	(void)src;
	printd(("%p T_UDERROR_IND\n", ip));
	if (!canputnext(ip->rq))
		goto ebusy;
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	mp->b_band = 2;		/* XXX move ahead of data indications */
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_UDERROR_IND;
	p->DEST_length = dst_len;
	p->DEST_offset = dst_len ? sizeof(*p) : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + dst_len : 0;
	p->ERROR_type = etype;
	if (dst_len) {
		bcopy(dst, mp->b_wptr, dst_len);
		mp->b_wptr += dst_len;
	}
	if (opt_len) {
		inet_build_opts(opts, &mp->b_wptr);
	}
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (err);
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	return (err);
}

/* 
 *  T_OPTMGMT_ACK       22 - Options Management Acknowledge
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int t_optmgmt_ack(queue_t * q, ulong flags, inet_opts_t * opts)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_optmgmt_ack *p;
	size_t opt_len = inet_opts_size(opts);
	size_t msg_len = sizeof(*p) + opt_len;

	printd(("%p T_OPTMGMT_ACK\n", ip));
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_OPTMGMT_ACK;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	p->MGMT_flags = flags;
	if (opt_len) {
		inet_build_opts(opts, &mp->b_wptr);
	}
#ifdef TS_WACK_OPTREQ
	if (ip->state == TS_WACK_OPTREQ)
		ip->state = TS_IDLE;
#endif
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (err);
}

/* 
 *  T_ORDREL_IND        23 - Orderly Release Indication
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int t_ordrel_ind(queue_t * q)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_ordrel_ind *p;
	size_t msg_len = sizeof(*p);

	printd(("%p T_ORDREL_IND\n", ip));
	ensure(((1 << ip->state) & TSM_INDATA), return (-EFAULT));
	if (!canputnext(ip->rq))
		goto ebusy;
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_ORDREL_IND;
	switch (ip->state) {
	case TS_DATA_XFER:
		ip->state = TS_WREQ_ORDREL;
		break;
	case TS_WIND_ORDREL:
		ip->state = TS_IDLE;
		break;
	}
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (err);
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	return (err);
}

/* 
 *  T_OPTDATA_IND       26 - Data with Options Indication
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int t_optdata_ind(queue_t * q, uint flags, inet_opts_t * opts, mblk_t * dp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_optdata_ind *p;
	size_t opt_len = inet_opts_size(opts);
	size_t msg_len = sizeof(*p) + opt_len;

	printd(("%p T_OPTDATA_IND\n", ip));
	if (!canputnext(ip->rq))
		goto ebusy;
	if ((mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	mp->b_band = flags & T_ODF_EX ? 1 : 0;	/* expedite */
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_OPTDATA_IND;
	p->DATA_flag = flags & (T_ODF_MORE | T_ODF_EX);
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	if (opt_len) {
		inet_build_opts(opts, &mp->b_wptr);
	}
	mp->b_cont = dp;
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (err);
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	return (err);
}

#ifdef T_ADDR_ACK
/* 
 *  T_ADDR_ACK          27 - Address Acknowledgement
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int t_addr_ack(queue_t * q, inet_addr_t * loc, inet_addr_t * rem)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_addr_ack *p;
	size_t loc_len = loc ? sizeof(*loc) : 0;
	size_t rem_len = rem ? sizeof(*rem) : 0;
	size_t msg_len = sizeof(*p) + loc_len + rem_len;

	printd(("%p T_ADDR_ACK\n", ip));
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_ADDR_ACK;
	p->LOCADDR_length = loc_len;
	p->LOCADDR_offset = loc_len ? sizeof(*p) : 0;
	p->REMADDR_length = rem_len;
	p->REMADDR_offset = rem_len ? sizeof(*p) + loc_len : 0;
	if (loc_len) {
		bcopy(loc, mp->b_wptr, loc_len);
		mp->b_wptr += loc_len;
	}
	if (rem_len) {
		bcopy(rem, mp->b_wptr, rem_len);
		mp->b_wptr += rem_len;
	}
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (err);
}
#endif
#if T_CAPABILITY_ACK
/* 
 *  T_CAPABILITY_ACK    ?? - Protocol Capability Ack
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int t_capability_ack(queue_t * q, ulong caps)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_capability_ack *p;
	size_t msg_len = sizeof(*p);

	printd(("%p T_CAPABILITY_ACK\n", ip));
	if (!(mp = inet_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_CAPABILITY_ACK;
	p->CAP_bits1 = TC1_INFO;
	p->ACCPETOR_id = (caps & TC1_ACCEPTOR) ? (ulong) ip->rq : 0;
	if (caps & TC1_INFO) {
		p->INFO_ack.PRIM_type = T_INFO_ACK;
		p->INFO_ack.TSDU_size = ip->tsdu;
		p->INFO_ack.ETSDU_size = ip->etsdu;
		p->INFO_ack.CDATA_size = ip->cdata;
		p->INFO_ack.DDATA_size = ip->ddata;
		p->INFO_ack.ADDR_size = sizeof(inet_addr_t);
		p->INFO_ack.OPT_size = T_INFINITE;
		p->INFO_ack.TIDU_size = ip->tidu;
		p->INFO_ack.SERV_type = ip->serv_type;
		p->INFO_ack.CURRENT_state = ip->state;
		p->INFO_ack.PROVIDER_flag = XPG4_1 & ~T_SNDZERO;
	} else
		bzero(&p->INFO_ack, sizeof(p->INFO_ack));
	putnext(ip->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (err);
}
#endif
/* 
 *  =========================================================================
 *
 *  CONNECTION INNDICATIONS
 *
 *  =========================================================================
 */
/*
 *  CONIND
 *  -------------------------------------------------------------------------
 */
STATIC int inet_conn_ind(queue_t * q, mblk_t * mp)
{
	int err = -EFAULT;
	inet_t *ip = INET_PRIV(q);
	struct sock *newsk, *sk;
	struct socket *sock = NULL;
	inet_addr_t dst = { AF_UNSPEC, 0, };
	inet_opts_t opt = { 0, };

	ensure(ip, return (-EFAULT));
	ensure(ip->sock, return (-EFAULT));
	ensure(ip->sock->sk, return (-EFAULT));
	if ((1 << ip->state) & ~TSM_LISTEN)
		goto outstate;
	sk = ip->sock->sk;
	if (!ip->conind || sk->state != TCP_LISTEN)
		goto listen;
	if (mp->b_wptr < mp->b_rptr + sizeof(newsk))
		goto einval;
	newsk = *((struct sock **)mp->b_rptr);
	if (sk == newsk)
		goto multiple;
	if (t_seq_find(ip, mp))
		goto listen_bug;
	/* this might be the second time thru after a bufcall */
	if (newsk->state_change != inet_sk_state_change) {
		ensure(ip->sock->ops, return (-EFAULT));
		ensure(ip->sock->ops->accept, return (-EFAULT));
		if (!(sock = sock_alloc()))
			goto socket_err;
		sock->type = ip->sock->type;
		sock->ops = ip->sock->ops;
		if ((err = ip->sock->ops->accept(ip->sock, sock, O_NONBLOCK)) < 0)
			goto accept_err;
		if (!sock->sk)
			goto accept_bug;
		if (sock->sk != newsk) {
			ptrace(("ERROR: fixing up socket pointer\n"));
			*((struct sock **)mp->b_rptr) = sock->sk;
			newsk = sock->sk;
		}
		if (sock->sk->socket != sock) {
			ptrace(("ERROR: fixing up socket pointer\n"));
			sock->sk->socket = sock;
		}
		inet_socket_get(sock, NULL);
	}
	dst.sin_family = AF_INET;
	dst.sin_port = newsk->dport;
	dst.sin_addr.s_addr = newsk->daddr;
#ifdef LINUX_2_4
	opt.opt = newsk->protinfo.af_inet.opt;
	opt.tos = (ulong *) & newsk->protinfo.af_inet.tos;
	opt.ttl = (ulong *) & newsk->protinfo.af_inet.ttl;
#else
	opt.opt = newsk->opt;
	opt.tos = (ulong *) & newsk->ip_tos;
	opt.ttl = (ulong *) & newsk->ip_ttl;
#endif
	opt.reuse = (ulong *) & newsk->reuse;
	opt.norte = (ulong *) & newsk->localroute;
	opt.bcast = (ulong *) & newsk->broadcast;
	if (opt.opt)
		opt.flags |= TF_IP_OPTIONS;
	opt.flags |= TF_IP_TOS;
	opt.flags |= TF_IP_TTL;
	opt.flags |= TF_IP_REUSEADDR;
	opt.flags |= TF_IP_DONTROUTE;
	opt.flags |= TF_IP_BROADCAST;
	if (ip->proto == T_INET_TCP) {
#ifdef LINUX_2_4
		opt.nodly = (ulong *) & newsk->tp_pinfo.af_tcp.nonagle;
#else
		opt.nodly = (ulong *) & newsk->nonagle;
#endif
		opt.mss = (ulong *) & newsk->tp_pinfo.af_tcp.user_mss;
		opt.alive = (ulong *) & newsk->keepopen;
		opt.flags |= TF_TCP_NODELAY;
		opt.flags |= TF_TCP_MAXSEG;
		opt.flags |= TF_TCP_KEEPALIVE;
	}
	if (ip->proto == T_INET_SCTP) {
		/* skip for now */
	}
	return t_conn_ind(q, &dst, &opt, mp, NULL);
      accept_bug:
	err = -ECONNABORTED;
	inet_socket_put(sock);
	ptrace(("ERROR: accept() error (out of order conninds)\n"));
	return (err);
      accept_err:
	err = err;
	inet_socket_put(sock);
	ptrace(("ERROR: accept() error\n"));
	return (err);
      socket_err:
	err = -EMFILE;
	ptrace(("ERROR: socket() error\n"));
	return (err);
      listen_bug:
	err = -EFAULT;
	ptrace(("ERROR: listen() error (multiple conninds)\n"));
	return (err);
      multiple:
	err = QR_DONE;
	ptrace(("WARNING: multiple connection indications\n"));
	return (err);
      einval:
	err = -EFAULT;
	swerr();
	printd(("SWERR: invalid primitive format\n"));
	return (err);
      outstate:
	err = -EPROTO;
	ptrace(("ERROR: connect indication in wrong state %d\n", ip->state));
	return (err);
      listen:
	err = -EPROTO;
	ptrace(("ERROR: connect indication received while not listening\n"));
	return (err);
}

/* 
 *  =========================================================================
 *
 *  SENDMSG and RECVMSG
 *
 *  =========================================================================
 */
/* 
 *  SENDMSG
 *  -------------------------------------------------------------------------
 *  Convert the mblk to send into an iovec and the options into a control
 *  message and then call sendmsg on the socket with the kernel data segment.
 *  The socket will handle moving data from the mblks.
 */
STATIC int inet_sock_sendmsg(inet_t * ip, mblk_t * mp, inet_addr_t * dst, int flags,
			     inet_opts_t * opts)
{
	int rtn = 0;
	int len, sdu, n;
	mblk_t *dp;

	ensure(ip, return (-EFAULT));
	ensure(ip->sock, return (-EFAULT));

	for (len = 0, n = 0, dp = mp; dp; dp = dp->b_cont) {
		if (dp->b_datap->db_type == M_DATA && dp->b_wptr > dp->b_rptr) {
			len += dp->b_wptr - dp->b_rptr;
			n++;
		}
	}
	if (!(ip->prov_flags & T_SNDZERO) && (!n || !len))
		goto eproto;
	if ((sdu = (int)ip->tsdu) > 0 && (!n || len > sdu))
		goto emsgsize;
	if (len > sock_wspace(ip->sock->sk))
		goto ebusy;
	/* 
	 *  This has the ramification that we can never do zero length writes,
	 *  but we have ~T_SNDZERO set anyway.
	 */
	{
		int i;
		struct iovec iov[n];
		struct msghdr msg;
		size_t clen = inet_cmsg_size(opts, TF_IP_OPTIONS | TF_IP_PKTINFO);
		unsigned char cbuf[clen > 1024 ? 1024 : clen];

		flags |= (MSG_DONTWAIT | MSG_NOSIGNAL);
		flags |= ip->options.norte ? MSG_DONTROUTE : 0;
		msg.msg_name = dst;
		msg.msg_namelen = dst ? sizeof(*dst) : 0;
		msg.msg_iov = iov;
		msg.msg_iovlen = n;
		msg.msg_control = cbuf;
		msg.msg_controllen = clen;
		msg.msg_flags = flags;
		/* 
		 * convert message blocks to an iovec 
		 */
		for (i = 0, dp = mp; dp; dp = dp->b_cont) {
			if (dp->b_datap->db_type == M_DATA && dp->b_wptr > dp->b_rptr) {
				iov[i].iov_base = dp->b_rptr;
				iov[i].iov_len = dp->b_wptr - dp->b_rptr;
				i++;
			}
		}
		inet_build_cmsg(opts, &msg, TF_IP_OPTIONS | TF_IP_PKTINFO);
		opts->flags &= TM_OPT_SENDMSG;
		inet_opt_negotiate(ip, opts);
		{
			mm_segment_t fs = get_fs();

			set_fs(get_ds());
			ensure(ip->sock->ops, return (-EFAULT));
			ensure(ip->sock->ops->sendmsg, return (-EFAULT));
			rtn = ip->sock->ops->sendmsg(ip->sock, &msg, len, NULL);
			set_fs(fs);
			if (!rtn)
				goto ebusy;
			if (rtn == -ENOMEM)
				goto enomem;
		}
		if (rtn == len) {
			return (QR_DONE);	/* full write */
		}
		if (rtn > 0) {
			if (ip->type == SOCK_STREAM || ip->type == SOCK_SEQPACKET)
				goto eagain;
			goto eproto2;
		}
		goto sendmsg_err;
	}
      sendmsg_err:
	ptrace(("ERROR: sendmsg() error\n"));
	return (rtn);
      eagain:
	adjmsg(mp, rtn);
	rtn = -EAGAIN;
	ptrace(("WARNING: Partial write, writing more later\n"));
	return (rtn);
      eproto:
	rtn = -EPROTO;
	ptrace(("ERROR: zero data\n"));
	return (rtn);
      eproto2:
	rtn = -EPROTO;
	ptrace(("ERROR: illegal partial write\n"));
	return (rtn);
      ebusy:
	rtn = -EBUSY;
	ptrace(("INFO: flow controlled\n"));
	return (rtn);
      emsgsize:
	rtn = -EMSGSIZE;
	ptrace(("ERROR: message to large\n"));
	return (rtn);
      enomem:
	/* 
	 *  This buffer call is just to kick us.  Because LiS uses kmalloc for
	 *  mblks, if we can allocate an mblk, then another kernel routine can
	 *  allocate that much memory too.
	 */
	rtn = -ENOMEM;
	inet_bufcall(ip->wq, len, BPRI_LO);
	return (rtn);
}

/* 
 *  RECVMSG
 *  -------------------------------------------------------------------------
 */
#if 0
STATIC INLINE size_t inet_rx_data_amount(inet_t * ip)
{
	size_t size = 0;

	switch (ip->type) {
	case SOCK_DGRAM:
	case SOCK_RAW:
	case SOCK_SEQPACKET:
	{
		/* 
		 *  For datagram and raw packets things are rather
		 *  straightforward.
		 */
		struct sk_buff *skb;

		size = (skb = skb_peek(&ip->sock->sk->receive_queue)) ? skb->len : 0;
		break;
	}
	case SOCK_STREAM:
		/* 
		 *  For these calculating the available size is more difficult
		 *  but not impossible.  Here's a quick estimate: we take the
		 *  read memory allocated to the socket: the way that linux
		 *  calculates this, it will be a little large, but that's ok.
		 *
		 *  Actually, this would work for datagram too...
		 */
		size = atomic_read(&ip->sock->sk->rmem_alloc);
		break;
	}
	return (size);
}
#endif
STATIC INLINE void inet_sock_recvmsg(queue_t * q)
{
	inet_t *ip = INET_PRIV(q);
	int i, n, rem, more, expd, err;
	size_t size = 0;
	mblk_t *mp, *db;
	unsigned char cbuf[128] = { 0, };
	struct msghdr msg = { NULL, };
	inet_addr_t src = { AF_UNSPEC, 0 };
	inet_opts_t opt = { 0, };

	ensure(ip, return);
	ensure(ip->sock, return);
	ensure(ip->sock->sk, return);

	if (!(size = atomic_read(&ip->sock->sk->rmem_alloc)))
		goto eagain;
#if 0
	if (!(size = inet_rx_data_amount(ip)))
		goto eagain;
#endif
	if (!(mp = inet_allocb(ip->rq, size, BPRI_MED)))
		goto enobufs;
	for (n = 0, db = mp; db; db = db->b_cont)
		if (db->b_datap->db_type == M_DATA)
			n++;
	if (n == 0)
		goto emsgsize;
	{
		struct iovec iov[n];

		for (i = 0, db = mp, rem = size; db; db = db->b_cont) {
			if (db->b_datap->db_type == M_DATA) {
				int len = db->b_datap->db_lim - db->b_rptr;

				if (len > rem)
					len = rem;
				db->b_wptr += len;
				rem -= len;
				iov[i].iov_base = db->b_rptr;
				iov[i].iov_len = len;
			}
		}
		src.sin_family = AF_INET;
		src.sin_port = 0;
		src.sin_addr.s_addr = 0;
		msg.msg_name = &src;
		msg.msg_namelen = sizeof(src);
		msg.msg_iov = iov;
		msg.msg_iovlen = n;
		msg.msg_control = cbuf;
		msg.msg_controllen = sizeof(cbuf);
		msg.msg_flags = 0;
		{
			int sflags = MSG_DONTWAIT | MSG_NOSIGNAL;
			mm_segment_t fs = get_fs();

			set_fs(get_ds());
			ensure(ip->sock->ops, err = -EFAULT;
			       goto recvmsg_err);
			ensure(ip->sock->ops->recvmsg, err = -EFAULT;
			       goto recvmsg_err);
			err = ip->sock->ops->recvmsg(ip->sock, &msg, size, sflags, NULL);
			set_fs(fs);
			if (err < 0)
				goto recvmsg_err;
		}
	}
	if (msg.msg_flags & MSG_TRUNC)
		goto trunc_err;
	if (msg.msg_controllen)
		inet_parse_cmsg(&msg, &opt);
	if ((err - size) < 0)
		adjmsg(mp, err - size);
	more = (msg.msg_flags & MSG_EOR) ? 0 : T_ODF_MORE;
	expd = (msg.msg_flags & MSG_OOB) ? T_ODF_EX : 0;
	switch (ip->state) {
	case TS_IDLE:
		if ((err = t_unitdata_ind(q, &src, &opt, mp)))
			goto unitdata_err;
		break;
	case TS_DATA_XFER:
	case TS_WIND_ORDREL:
		if (!opt.flags) {
			if (msg.msg_flags & MSG_OOB) {
				if ((err = t_exdata_ind(q, more, mp)))
					goto exdata_err;
			} else {
				if ((err = t_data_ind(q, more, mp)))
					goto data_err;
			}
		} else {
			if ((err = t_optdata_ind(q, more | expd, &opt, mp)))
				goto optdata_err;
		}
		break;
	}
	return (void)(0);
      unitdata_err:
	freemsg(mp);
	ptrace(("ERROR: T_UNITDATA_IND error\n"));
	return (void)(err);
      exdata_err:
	ptrace(("ERROR: T_EXDATA_IND error\n"));
	freemsg(mp);
	return (void)(err);
      data_err:
	ptrace(("ERROR: T_DATA_IND error\n"));
	freemsg(mp);
	return (void)(err);
      optdata_err:
	ptrace(("ERROR: T_OPTDATA_IND error\n"));
	freemsg(mp);
	return (void)(err);
      trunc_err:
	err = -EMSGSIZE;
	freemsg(mp);
	ptrace(("ERROR: recvmsg() error (truncated)\n"));
	return (void)(err);
      recvmsg_err:
	freemsg(mp);
	ptrace(("ERROR: recvmsg() error\n"));
	return (void)(err);
      emsgsize:
	err = -EMSGSIZE;
	freemsg(mp);
	ptrace(("ERROR: recvmsg() error\n"));
	return (void)(err);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	return (void)(err);
      eagain:
	err = -EAGAIN;
	ptrace(("ERROR: No data ready\n"));
	return (void)(err);
}

/* 
 *  =========================================================================
 *
 *  STATE MACHINE
 *
 *  =========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  WAKEUPS
 *
 *  -------------------------------------------------------------------------
 *  These functions are called by the read and write queue service routines
 *  just before locks are released.  The purpose is to activate state changes
 *  that result from the socket being enabled for read or enabled for write.
 *  Connecting sockets are enabled for write once they have been successfully
 *  connected.  Listening sockets are enabled for read when a connection
 *  indication is ready to be accepted.  Otherwise, enabling of the read and
 *  write queues is performed when more data can be read or written
 *  respectively.
 */
/*
 *  TX WAKEUP
 *  ------------------------------------
 *  We only have to handle the special case where an enable for write
 *  indicates that a connecting socket has successfully connected.
 */
STATIC INLINE void inet_tx_wakeup(queue_t * q)
{
	return;
}

/*
 *  RX WAKEUP
 *  -------------------------------------------------------------------------
 *  We only have to handle the special case where an enable for read indicates
 *  that a listening socket is receiving a connection indication.  Aside from
 *  that, we must move any data available to be read to the read queue.
 */
STATIC INLINE void inet_rx_wakeup(queue_t * q)
{
	inet_t *ip = INET_PRIV(q);

	switch (ip->state) {
	case TS_WRES_CIND:
	case TS_IDLE:
		/*
		 *  NOTE: we have a connection indication on a
		 *  listening socket; however, we also get the state
		 *  change on the listening socket so we do nothing
		 *  here.
		 */
		if (ip->conind)
			break;
	case TS_DATA_XFER:
	case TS_WIND_ORDREL:
		/* we are reading data */
		inet_sock_recvmsg(q);
		break;
	default:
		swerr();
		printd(("SWERR: Rx wakeup for invalid state(%u)\n", ip->state));
		break;
	}
}

/*
 *  WAKEUP
 *  -------------------------------------------------------------------------
 */
STATIC INLINE void inet_wakeup(queue_t * q)
{
	inet_t *ip = INET_PRIV(q);

	if (q == ip->rq)
		return inet_rx_wakeup(q);
	if (q == ip->wq)
		return inet_tx_wakeup(q);
	return;
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  Socket CALLBACKS
 *
 *  -------------------------------------------------------------------------
 */
STATIC INLINE void inet_putctl(inet_t * ip, queue_t * q, int type, void (*func) (long),
			       struct sock *sk)
{
	mblk_t *mp;

	if ((mp = allocb(sizeof(sk), BPRI_HI))) {
		mp->b_datap->db_type = type;
		*((struct sock **)mp->b_wptr)++ = sk;
		putq(q, mp);
		return (void)(0);
	}
	/*
	 *  We have to set up a buff call to call us again or we will lose the
	 *  event.
	 */
	ptrace(("ERROR: no buffers\n"));
	if (q == ip->rq)
		ip->rbid = bufcall(sizeof(sk), BPRI_HI, func, (long)sk);
	if (q == ip->wq)
		ip->wbid = bufcall(sizeof(sk), BPRI_HI, func, (long)sk);
	return (void)(-ENOBUFS);
}

/* 
 *  STATE change
 *  -------------------------------------------------------------------------
 *  We get state changes on sockets that we hold.  We also get state changes
 *  on accepting sockets.
 */
STATIC void _inet_sock_state_change(long data)
{
	inet_t *ip;
	struct sock *sk;

	sk = (struct sock *)data;
	ensure(sk, return);
	read_lock(&sk->callback_lock);
	do {
		ip = (inet_t *) sk->user_data;
		ensure(ip, break);
		ensure(ip->sock, break);
		ensure(ip->sock->sk, break);
		printd(("%p State Change\n", ip));
		inet_putctl(ip, ip->rq, M_PCRSE, &_inet_sock_state_change, sk);
	} while (0);
	read_unlock(&sk->callback_lock);
}
STATIC void inet_sk_state_change(struct sock *sk)
{
	_inet_sock_state_change((long)sk);
}

/* 
 *  WRITE Available
 *  -------------------------------------------------------------------------
 */
STATIC void _inet_sock_writeable(long data)
{
	inet_t *ip;
	struct sock *sk;

	sk = (struct sock *)data;
	ensure(sk, return);
	read_lock(&sk->callback_lock);
	do {
		ip = (inet_t *) sk->user_data;
		ensure(ip, break);
		ensure(ip->sock, break);
		ensure(ip->sock->sk, break);
		assure(ip->sock->sk == sk);
		printd(("%p Write Space\n", ip));
		inet_putctl(ip, ip->wq, M_READ, &_inet_sock_writeable, sk);
	} while (0);
	read_unlock(&sk->callback_lock);
}
STATIC void inet_sk_writeable(struct sock *sk)
{
	_inet_sock_writeable((long)sk);
}

/* 
 *  ERROR Available
 *  -------------------------------------------------------------------------
 */
STATIC void _inet_sock_errors(long data)
{
	inet_t *ip;
	struct sock *sk;

	sk = (struct sock *)data;
	ensure(sk, return);
	read_lock(&sk->callback_lock);
	do {
		ip = (inet_t *) sk->user_data;
		ensure(ip, break);
		ensure(ip->sock, break);
		ensure(ip->sock->sk, break);
		printd(("%p Socket Error\n", ip));
		inet_putctl(ip, ip->rq, M_ERROR, &_inet_sock_errors, sk);
	} while (0);
	read_unlock(&sk->callback_lock);
}
STATIC void inet_sk_errors(struct sock *sk)
{
	_inet_sock_errors((long)sk);
}

/* 
 *  READ Available
 *  -------------------------------------------------------------------------
 */
STATIC void _inet_sock_readable(long data)
{
	inet_t *ip;
	struct sock *sk;

	sk = (struct sock *)data;
	ensure(sk, return);
	read_lock(&sk->callback_lock);
	do {
		ip = (inet_t *) sk->user_data;
		ensure(ip, break);
		ensure(ip->sock, break);
		ensure(ip->sock->sk, break);
		printd(("%p Data Ready\n", ip));
		inet_putctl(ip, ip->rq, M_READ, &_inet_sock_readable, sk);
	} while (0);
	read_unlock(&sk->callback_lock);
}
STATIC void inet_sk_readable(struct sock *sk, int len)
{
	(void)len;
	_inet_sock_readable((long)sk);
}

/* 
 *  =========================================================================
 *
 *  IP T-User --> T-Provider Primitives (Request and Response)
 *
 *  =========================================================================
 *
 *  T_CONN_REQ           0 - TC Request
 *  -------------------------------------------------------------------
 */
STATIC int t_conn_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err = -EFAULT;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_conn_req *p = (typeof(p)) mp->b_rptr;
	inet_addr_t *dst;
	struct inet_opts opts = { 0L, NULL, };
	size_t opt_len, opt_off;
	unsigned char *opt;

	ensure(ip, return (-EFAULT));
	ensure(ip->sock, return (-EFAULT));

	printd(("%p T_CONN_REQ\n", ip));
	if (ip->state != TS_IDLE)
		goto outstate;
	ip->state = TS_WACK_CREQ;
	if (mlen < sizeof(*p))
		goto inval;
	dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
	if ((mlen < p->DEST_offset + p->DEST_length && p->DEST_length == sizeof(*dst))
	    || dst->sin_port == 0)
		goto badaddr;
	if (ip->cred.cr_uid != 0 && htons(dst->sin_port) == IPPROTO_RAW)
		goto acces;
	opt_len = p->OPT_length;
	opt_off = p->OPT_offset;
	opt = mp->b_rptr + opt_off;
	if (opt_len
	    && (mlen < opt_off + opt_len || (err = inet_parse_opts(ip, &opts, opt, opt_len))))
		goto badopt;
	/* 
	 * TODO: set options first 
	 */
	ensure(ip->sock->ops, return (-EFAULT));
	ensure(ip->sock->ops->connect, return (-EFAULT));
	if ((err =
	     ip->sock->ops->connect(ip->sock, (struct sockaddr *)dst, sizeof(*dst),
				    O_NONBLOCK))
	    && err != -EINPROGRESS)
		goto syserr;
	if (mp->b_cont != NULL) {
		putbq(q, mp->b_cont);	/* hold back data */
		mp->b_cont = NULL;	/* abosrbed mp->b_cont */
	}
	if ((err = t_ok_ack(q, T_CONN_REQ, 0, 0)))
		goto nobufs;
	if (ip->type == SOCK_DGRAM || ip->type == SOCK_RAW)
		qenable(ip->wq);	/* force a T_CONN_CON */
	return (0);
      nobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	goto error;
      syserr:
	err = err;
	ptrace(("ERROR: connect() error\n"));
	goto error;
      badopt:
	err = TBADOPT;
	ptrace(("ERROR: bad options\n"));
	goto error;
      acces:
	err = TACCES;
	ptrace(("ERROR: no permission for address\n"));
	goto error;
      badaddr:
	err = TBADADDR;
	ptrace(("ERROR: address is unusable\n"));
	goto error;
      inval:
	err = -EINVAL;
	ptrace(("ERROR: invalid message format\n"));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:
	return t_error_ack(q, T_CONN_REQ, err);
}

/* 
 *  T_CONN_RES           1 - Accept previous connection indication
 *  -------------------------------------------------------------------
 */
STATIC INLINE mblk_t *t_seq_check(inet_t * ip, ulong seq)
{
	mblk_t *mp = bufq_head(&ip->conq);

	for (; mp && mp != (mblk_t *) seq; mp = mp->b_next) ;
	usual(mp);
	return (mp);
}
STATIC INLINE inet_t *t_tok_check(ulong acceptor)
{
	inet_t *ap;
	queue_t *aq = (queue_t *) acceptor;

	for (ap = inet_opens; ap && ap->rq != aq; ap = ap->next) ;
	usual(ap);
	return (ap);
}
STATIC int t_conn_res(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err = 0;
	mblk_t *cp;
	inet_t *ap;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_conn_res *p = (typeof(p)) mp->b_rptr;
	struct inet_opts opts = { 0L, NULL, };
	size_t opt_len, opt_off;
	unsigned char *opt;

	printd(("%p T_CONN_RES\n", ip));
	if (ip->state != TS_WRES_CIND)
		goto outstate;
	ip->state = TS_WACK_CRES;
	if (mlen < sizeof(*p))
		goto inval;
	if ((cp = t_seq_check(ip, p->SEQ_number)) == NULL)
		goto badseq;
	if (((ap = t_tok_check(p->ACCEPTOR_id)) == NULL)
	    || !(ap == ip || ((1 << ap->state) & TSM_DISCONN)))
		goto badf;
	if (ip->proto != ap->proto)
		goto provmismatch;
	if (ap->state == TS_IDLE && ap->conind)
		goto resqlen;
	if (ip->cred.cr_uid != 0 && ap->cred.cr_uid == 0)
		goto acces;
	opt_len = p->OPT_length;
	opt_off = p->OPT_offset;
	opt = mp->b_rptr + opt_off;
	if (opt_len
	    && (mlen < opt_off + opt_len || (err = inet_parse_opts(ip, &opts, opt, opt_len))))
		goto badopt;
	if (mp->b_cont != NULL && msgdsize(mp->b_cont))
		goto baddata;
	return t_ok_ack(q, T_CONN_RES, (ulong) cp, (ulong) ap);
      baddata:
	err = TBADDATA;
	ptrace(("ERROR: bad connection data\n"));
	goto error;
      badopt:
	err = TBADOPT;
	ptrace(("ERROR: bad options\n"));
	goto error;
      acces:
	err = TACCES;
	ptrace(("ERROR: no access to accepting queue\n"));
	goto error;
      resqlen:
	err = TRESQLEN;
	ptrace(("ERROR: accepting queue is listening\n"));
	goto error;
      provmismatch:
	err = TPROVMISMATCH;
	ptrace(("ERROR: not same transport provider\n"));
	goto error;
      badf:
	err = TBADF;
	ptrace(("ERROR: accepting queue id is invalid\n"));
	goto error;
      badseq:
	err = TBADSEQ;
	ptrace(("ERROR: sequence number is invalid\n"));
	goto error;
      inval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:
	return t_error_ack(q, T_CONN_RES, err);
}

/* 
 *  T_DISCON_REQ         2 - TC disconnection request
 *  -------------------------------------------------------------------
 */
STATIC int t_discon_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	mblk_t *cp = NULL;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	struct T_discon_req *p = (typeof(p)) mp->b_rptr;

	printd(("%p T_DISCON_REQ\n", ip));
	if ((1 << ip->state) & ~TSM_CONNECTED)
		goto outstate;
	switch (ip->state) {
	case TS_WCON_CREQ:
		ip->state = TS_WACK_DREQ6;
		break;
	case TS_WRES_CIND:
		ip->state = TS_WACK_DREQ7;
		break;
	case TS_DATA_XFER:
		ip->state = TS_WACK_DREQ9;
		break;
	case TS_WIND_ORDREL:
		ip->state = TS_WACK_DREQ10;
		break;
	case TS_WREQ_ORDREL:
		ip->state = TS_WACK_DREQ11;
		break;
	}
	if (mlen < sizeof(*p))
		goto einval;
	if (ip->state == TS_WACK_DREQ7 && !(cp = t_seq_check(ip, p->SEQ_number)))
		goto badseq;
	return t_ok_ack(q, T_DISCON_REQ, p->SEQ_number, 0);
      badseq:
	err = TBADSEQ;
	ptrace(("ERROR: sequence number is invalid\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:
	return t_error_ack(q, T_DISCON_REQ, err);
}

/* 
 *  T_DATA_REQ           3 - Connection-Mode data transfer request
 *  -------------------------------------------------------------------
 */
STATIC INLINE int m_error(queue_t * q, int error)
{
	mblk_t *mp;

	if (error == EPIPE) {
		ptrace(("ERROR: hangup\n"));
		if ((mp = inet_allocb(q, 0, BPRI_HI))) {
			mp->b_datap->db_type = M_HANGUP;
			putnext(q, mp);
			return (0);
		}
	} else {
		ptrace(("ERROR: fatal error %d\n", error));
		if ((mp = inet_allocb(q, 2, BPRI_HI))) {
			mp->b_datap->db_type = M_ERROR;
			*(mp->b_wptr)++ = error;
			*(mp->b_wptr)++ = error;
			putnext(q, mp);
			return (0);
		}
	}
	return (-ENOBUFS);
}
STATIC int m_error_reply(queue_t * q, int error)
{
	inet_t *ip = INET_PRIV(q);

	switch (error) {
	case -ENOMEM:
	case -ENOBUFS:
	case -EBUSY:
	case -EAGAIN:
		seldom();
		return (error);
	case QR_DONE:
#if 0
	case QR_ABSORBED:
	case QR_TRIMMED:
	case QR_LOOP:
	case QR_PASSALONG:
	case QR_PASSFLOW:
	case QR_DISABLE:
		never();
#endif
		return (error);
		/*
		 *  FIXME: move these to inet_sock_sendmsg().
		 */
	case -EHOSTUNREACH:
		/* 
		 * FIXME: should probably be &ip->err for error destination 
		 */
		return t_uderror_ind(q, &ip->src, &ip->dst, NULL, NULL, -error);
	case -EPIPE:
	case -ENETDOWN:
	case -ENETUNREACH:
		return m_error(ip->rq, EPIPE);
	default:
	case -EPROTO:
	case -EINVAL:
	case -EACCES:
	case -ENODEV:
	case -EFAULT:
	case -EPERM:
	case -EMSGSIZE:	/* does this include fragmentation with DF bit? */
	case -ENOTCONN:
	case -ERESTART:
	case -ERESTARTSYS:
		return m_error(ip->rq, -error);
	}
}
STATIC int inet_w_data(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	int flags;

	if (ip->state != TS_IDLE)
		goto discard;
	if ((1 << ip->state) & ~TSM_OUTDATA)
		goto eproto;
	flags = (ip->type == SOCK_SEQPACKET) ? MSG_EOR : 0;
	err = inet_sock_sendmsg(ip, mp, NULL, flags, NULL);
      error:
	return m_error_reply(q, err);
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      discard:
	err = QR_DONE;
	ptrace(("ERROR: ignore in idle state\n"));
	goto error;
}
STATIC int t_data_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	int flags;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_data_req *p = (typeof(p)) mp->b_rptr;

	printd(("%p T_DATA_REQ\n", ip));
	if (ip->state == TS_IDLE)
		goto discard;
	if (mlen < sizeof(*p))
		goto einval;
	flags = (ip->type == SOCK_SEQPACKET || !p->MORE_flag) ? MSG_EOR : 0;
	if ((1 << ip->state) & ~TSM_OUTDATA)
		goto eproto;
	err = inet_sock_sendmsg(ip, mp, NULL, flags, NULL);
      error:
	return m_error_reply(q, err);
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      discard:
	err = QR_DONE;
	ptrace(("ERROR: ignore in idle state\n"));
	goto error;
}

/* 
 *  T_EXDATA_REQ         4 - Expedited data request
 *  -------------------------------------------------------------------
 */
STATIC int t_exdata_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	int flags;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_exdata_req *p = (typeof(p)) mp->b_rptr;

	printd(("%p T_EXDATA_REQ\n", ip));
	if (ip->state == TS_IDLE)
		goto discard;
	if (mlen < sizeof(*p))
		goto einval;
	if ((1 << ip->state) & ~TSM_OUTDATA)
		goto eproto;
	flags = MSG_OOB | (ip->type == SOCK_SEQPACKET || !p->MORE_flag) ? MSG_EOR : 0;
	err = inet_sock_sendmsg(ip, mp, NULL, flags, NULL);
      error:
	return m_error_reply(q, err);
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      discard:
	err = QR_DONE;
	ptrace(("ERROR: ignore in idle state\n"));
	goto error;
}

/* 
 *  T_INFO_REQ           5 - Information Request
 *  -------------------------------------------------------------------
 */
STATIC int t_info_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);

	(void)mp;
	(void)ip;
	printd(("%p T_INFO_REQ\n", ip));
	return t_info_ack(q);
}

/* 
 *  T_BIND_REQ           6 - Bind a TS user to a transport address
 *  -------------------------------------------------------------------
 */
STATIC int t_bind_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_bind_req *p = (typeof(p)) mp->b_rptr;
	inet_addr_t *add;
	const size_t add_len = sizeof(*add);
	unsigned short proto;

	ensure(ip, return (-EFAULT));
	unless(ip->sock, return (-EFAULT));

	printd(("%p T_BIND_REQ\n", ip));
	if (ip->state != TS_UNBND)
		goto outstate;
	ip->state = TS_WACK_BREQ;
	if (mlen < sizeof(*p))
		goto einval;
	if ((mlen < p->ADDR_offset + p->ADDR_length)
	    || (p->ADDR_length != add_len))
		goto badaddr;
	/* 
	 * we don't allow wildcards yet 
	 */
	add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
	if (!add->sin_port)
		goto noaddr;
	if (ip->type == SOCK_RAW && ip->cred.cr_uid != 0)
		goto acces;
	if ((proto = ip->proto) == IPPROTO_RAW)
		proto = htons(add->sin_port);
	if ((err = sock_create(ip->family, ip->type, proto, &ip->sock)))
		goto socket_err;
	ensure(ip->sock, err = -EFAULT;
	       goto socket_err);
	ensure(ip->sock->sk, err = -EFAULT;
	       goto socket_err);
	if (ip->sock->sk->socket != ip->sock) {
		ptrace(("ERROR: fixing up socket pointer\n"));
		ip->sock->sk->socket = ip->sock;
	}
	ensure(ip->sock, return (-EFAULT));
	ensure(ip->sock->sk, return (-EFAULT));
	inet_socket_get(ip->sock, ip);
	ensure(ip->sock->ops, return (-EFAULT));
	ensure(ip->sock->ops->bind, return (-EFAULT));
	ensure(ip->sock->ops->listen, return (-EFAULT));
	if ((err = ip->sock->ops->bind(ip->sock, (struct sockaddr *)add, sizeof(*add))))
		goto bind_err;
	if (p->CONIND_number) {
		ip->conind = 0;
		if (ip->type == SOCK_STREAM || ip->type == SOCK_SEQPACKET) {
			if ((err = ip->sock->ops->listen(ip->sock, p->CONIND_number)))
				goto listen_err;
			ip->conind = p->CONIND_number;
		}
	}
	ip->src = *add;
	return t_bind_ack(q, &ip->src);
      listen_err:
	inet_socket_put(xchg(&ip->sock, NULL));
	ptrace(("ERROR: listen() error\n"));
	goto error;
      bind_err:
	inet_socket_put(xchg(&ip->sock, NULL));
	ptrace(("ERROR: bind() error\n"));
	goto error;
      socket_err:
	err = err;
	ptrace(("ERROR: socket() error\n"));
	goto error;
      acces:
	err = TACCES;
	ptrace(("ERROR: no permission for address\n"));
	goto error;
      noaddr:
	err = TNOADDR;
	ptrace(("ERROR: couldn't allocate address\n"));
	goto error;
      badaddr:
	err = TBADADDR;
	ptrace(("ERROR: address is invalid\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:
	return t_error_ack(q, T_BIND_REQ, err);
}

/* 
 *  T_UNBIND_REQ         7 - Unbind TS user from transport address
 *  -------------------------------------------------------------------
 */
STATIC int t_unbind_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	const struct T_unbind_req *p = (typeof(p)) mp->b_rptr;

	(void)p;

	ensure(ip, return (-EFAULT));
	ensure(ip->sock, return (-EFAULT));
	ensure(ip->sock->sk, return (-EFAULT));

	printd(("%p T_UNBIND_REQ\n", ip));
	if (ip->state != TS_IDLE)
		goto outstate;
	ip->state = TS_WACK_UREQ;
	inet_socket_put(xchg(&ip->sock, NULL));
	return t_ok_ack(q, T_UNBIND_REQ, 0, 0);
      outstate:
	err = TOUTSTATE;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:
	return t_error_ack(q, T_UNBIND_REQ, err);
}

/* 
 *  T_UNITDATA_REQ       8 -Unitdata Request 
 *  -------------------------------------------------------------------
 */
STATIC int t_unitdata_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err = 0;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	size_t dlen = mp->b_cont ? msgdsize(mp->b_cont) : 0;
	const struct T_unitdata_req *p = (typeof(p)) mp->b_rptr;
	int flags;
	inet_addr_t *dst;
	struct inet_opts opts = { 0L, NULL, };
	size_t opt_len, opt_off;
	unsigned char *opt;

	printd(("%p T_UNITDATA_IND\n", ip));
	if (dlen > 0 && dlen <= ip->mtu)
		goto baddata;
	if (ip->state != TS_IDLE)
		goto outstate;
	if (mlen < sizeof(*p))
		goto einval;
	if ((mlen < p->DEST_offset + p->DEST_length)
	    || (p->DEST_length != sizeof(*dst)))
		goto badadd;
	if (ip->type == SOCK_RAW && ip->cred.cr_uid != 0)
		goto acces;
	opt_len = p->OPT_length;
	opt_off = p->OPT_offset;
	opt = mp->b_rptr + opt_off;
	if (opt_len
	    && (mlen < opt_off + opt_len || (err = inet_parse_opts(ip, &opts, opt, opt_len))))
		goto badopt;
	dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
	flags = (opts.norte && *(opts.norte)) ? MSG_DONTROUTE : 0;
	err = inet_sock_sendmsg(ip, mp, dst, flags, &opts);
      error:
	return m_error_reply(q, err);
      badopt:
	err = -EPROTO;
	ptrace(("ERROR: bad options\n"));
	goto error;
      acces:
	err = -EPROTO;
	ptrace(("ERROR: no permission to address\n"));
	goto error;
      badadd:
	err = -EPROTO;
	ptrace(("ERROR: bad destination address\n"));
	goto error;
      einval:
	err = -EPROTO;
	ptrace(("ERROR: invalid primitive\n"));
	goto error;
      outstate:
	err = -EPROTO;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      baddata:
	err = -EPROTO;
	ptrace(("ERROR: bad data size\n"));
	goto error;
}

/* 
 *  T_OPTMGMT_REQ        9 - Options management request
 *  -------------------------------------------------------------------
 */
STATIC int t_optmgmt_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err = 0;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_optmgmt_req *p = (typeof(p)) mp->b_rptr;
	struct inet_opts opts = { 0L, NULL, };
	size_t opt_len, opt_off;
	unsigned char *opt;

	printd(("%p T_OPTMGMT_REQ\n", ip));
#ifdef TS_WACK_OPTREQ
	if (ip->state == TS_IDLE)
		ip->state = TS_WACK_OPTREQ;
#endif
	if (mlen < sizeof(*p))
		goto einval;
	opt_len = p->OPT_length;
	opt_off = p->OPT_offset;
	opt = mp->b_rptr + opt_off;
	if (opt_len
	    && (mlen < opt_off + opt_len || (err = inet_parse_opts(ip, &opts, opt, opt_len))))
		goto badopt;
	switch (p->MGMT_flags) {
	case T_CHECK:
		err = inet_opt_check(ip, &opts);
		break;
	case T_NEGOTIATE:
		if (!opts.flags)
			inet_opt_default(ip, &opts);
		else if ((err = inet_opt_check(ip, &opts)))
			break;
		err = inet_opt_negotiate(ip, &opts);
		break;
	case T_DEFAULT:
		err = inet_opt_default(ip, &opts);
		break;
	case T_CURRENT:
		err = inet_opt_current(ip, &opts);
		break;
	default:
		goto badflag;
	}
	if (err)
		goto provspec;
	return t_optmgmt_ack(q, p->MGMT_flags, &opts);
      provspec:
	err = err;
	ptrace(("ERROR: provider specific\n"));
	goto error;
      badflag:
	err = TBADFLAG;
	ptrace(("ERROR: bad options flags\n"));
	goto error;
      badopt:
	err = TBADOPT;
	ptrace(("ERROR: bad options\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      error:
	return t_error_ack(q, T_OPTMGMT_REQ, err);
}

/* 
 *  T_ORDREL_REQ        10 - TS user is finished sending
 *  -------------------------------------------------------------------
 */
STATIC int t_ordrel_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_ordrel_req *p = (typeof(p)) mp->b_rptr;

	(void)mlen;

	ensure(ip, return (-EFAULT));
	ensure(ip->sock, return (-EFAULT));
	ensure(ip->sock->sk, return (-EFAULT));

	printd(("%p T_ORDREL_REQ\n", ip));
	if ((1 << ip->state) & ~TSM_OUTDATA)
		goto eproto;
	ensure(ip->sock->ops, return (-EFAULT));
	ensure(ip->sock->ops->shutdown, return (-EFAULT));
	if ((err = ip->sock->ops->shutdown(ip->sock, SEND_SHUTDOWN)))
		goto shutdown_err;
	return (0);
      shutdown_err:
	err = err;
	ptrace(("ERROR: shutdown() error\n"));
	goto error;
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:
	return m_error_reply(q, err);
}

/* 
 *  T_OPTDATA_REQ       24 - Data with options request
 *  -------------------------------------------------------------------
 */
STATIC int t_optdata_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	int err;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_optdata_req *p = (typeof(p)) mp->b_rptr;
	int flags;
	struct inet_opts opts = { 0L, NULL, };
	size_t opt_len, opt_off;
	unsigned char *opt;

	printd(("%p T_OPTDATA_REQ\n", ip));
	if (ip->state == TS_IDLE)
		goto discard;
	if (mlen < sizeof(*p))
		goto einval;
	if ((1 << ip->state) & ~TSM_OUTDATA)
		goto eproto;
	opt_len = p->OPT_length;
	opt_off = p->OPT_offset;
	opt = mp->b_rptr + opt_off;
	if (opt_len
	    && (mlen < opt_off + opt_len || (err = inet_parse_opts(ip, &opts, opt, opt_len))))
		goto badopt;
	flags = 0;
	if (opts.norte && *(opts.norte))
		flags |= MSG_DONTROUTE;
	if ((p->DATA_flag & T_ODF_EX))
		flags |= MSG_OOB;
	if (!(p->DATA_flag & T_ODF_MORE) && ip->type == SOCK_SEQPACKET)
		flags |= MSG_EOR;
	err = inet_sock_sendmsg(ip, mp, NULL, flags, &opts);
      error:
	return m_error_reply(q, err);
      badopt:
	err = TBADOPT;
	ptrace(("ERROR: bad options\n"));
	goto error;
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      discard:
	err = QR_DONE;
	ptrace(("ERROR: ignore in idle state\n"));
	goto error;
}

#ifdef T_ADDR_REQ
/* 
 *  T_ADDR_REQ          25 - Address Request
 *  -------------------------------------------------------------------
 */
STATIC int t_addr_req(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);

	(void)mp;
	printd(("%p T_ADDR_REQ\n", ip));
	switch (ip->state) {
	case TS_UNBND:
		return t_addr_ack(q, NULL, NULL);
	case TS_IDLE:
		return t_addr_ack(q, &ip->src, NULL);
	case TS_WCON_CREQ:
	case TS_DATA_XFER:
	case TS_WIND_ORDREL:
	case TS_WREQ_ORDREL:
		return t_addr_ack(q, &ip->src, &ip->dst);
	case TS_WRES_CIND:
		return t_addr_ack(q, NULL, &ip->dst);
	}
	return t_error_ack(q, T_ADDR_REQ, TOUTSTATE);
}
#endif
#ifdef T_CAPABILITY_REQ
/* 
 *  T_CAPABILITY_REQ    ?? - Capability Request
 *  -------------------------------------------------------------------
 */
#endif
/* 
 *  Other primitives    XX - other invalid primitives
 *  -------------------------------------------------------------------------
 */
STATIC int t_other_req(queue_t * q, mblk_t * mp)
{
	ulong prim = *((ulong *) mp->b_rptr);

	return t_error_ack(q, prim, TNOTSUPPORT);
}

/* 
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int inet_w_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	inet_t *ip = INET_PRIV(q);
	ulong oldstate = ip->state;

	switch ((prim = *((ulong *) mp->b_rptr))) {
	case T_CONN_REQ:
		rtn = t_conn_req(q, mp);
		break;
	case T_CONN_RES:
		rtn = t_conn_res(q, mp);
		break;
	case T_DISCON_REQ:
		rtn = t_discon_req(q, mp);
		break;
	case T_DATA_REQ:
		rtn = t_data_req(q, mp);
		break;
	case T_EXDATA_REQ:
		rtn = t_exdata_req(q, mp);
		break;
	case T_INFO_REQ:
		rtn = t_info_req(q, mp);
		break;
	case T_BIND_REQ:
		rtn = t_bind_req(q, mp);
		break;
	case T_UNBIND_REQ:
		rtn = t_unbind_req(q, mp);
		break;
	case T_OPTMGMT_REQ:
		rtn = t_optmgmt_req(q, mp);
		break;
	case T_UNITDATA_REQ:
		rtn = t_unitdata_req(q, mp);
		break;
	case T_ORDREL_REQ:
		rtn = t_ordrel_req(q, mp);
		break;
	case T_OPTDATA_REQ:
		rtn = t_optdata_req(q, mp);
		break;
#ifdef T_ADDR_REQ
	case T_ADDR_REQ:
		rtn = t_addr_req(q, mp);
		break;
#endif
#ifdef T_CAPABILITY_REQ
	case T_CAPABILITY_REQ:
		rtn = t_capability_req(q, mp);
		break;
#endif
	default:
		rtn = t_other_req(q, mp);
		break;
	}
	if (rtn < 0) {
		rare();
		ip->state = oldstate;
	}
	return (rtn);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int inet_m_flush(queue_t * q, mblk_t * mp, const uint8_t flag,
			       const uint8_t oflag)
{
	if (mp->b_rptr[0] & flag) {
		if (mp->b_rptr[0] & FLUSHBAND)
			flushband(q, mp->b_rptr[1], FLUSHALL);
		else
			flushq(q, FLUSHALL);
		if (q->q_next) {
			putnext(q, mp);
			return (QR_ABSORBED);
		}
		mp->b_rptr[0] &= ~flag;
	}
	if (mp->b_rptr[0] & oflag) {
		if (mp->b_rptr[0] & FLUSHBAND)
			flushband(OTHER(q), mp->b_rptr[1], FLUSHALL);
		else
			flushq(OTHER(q), FLUSHALL);
		if (OTHER(q)->q_next) {
			qreply(q, mp);
			return (QR_ABSORBED);
		}
	}
	return (0);
}
STATIC int inet_w_flush(queue_t * q, mblk_t * mp)
{
	return inet_m_flush(q, mp, FLUSHW, FLUSHR);
}
STATIC int inet_r_flush(queue_t * q, mblk_t * mp)
{
	return inet_m_flush(q, mp, FLUSHR, FLUSHW);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_PCRSE Handling
 *
 *  -------------------------------------------------------------------------
 *  This priority STREAMS message is used to signal a socket state change to
 *  the STREAMS portion of the INET driver.  We always examine both the state
 *  of the socket and the state of the TPI interface to determine what actions
 *  neet to be performed.
 */
STATIC int inet_r_pcrse(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	struct sock *sk = *((struct sock **)mp->b_rptr);

	ensure(ip, return (-EFAULT));
	ensure(sk, return (-EFAULT));
	unless(sk->err, return (-EFAULT));
	switch (ip->type) {
	case SOCK_STREAM:
	case SOCK_SEQPACKET:
		switch (ip->state) {
		case TS_WCON_CREQ:
			if (sk->state == TCP_ESTABLISHED)
				return ctrace(t_conn_con(q, &ip->dst, NULL, NULL));
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind
					   (q, &ip->dst, T_PROVIDER, 0, NULL, NULL));
			swerr();
			break;
		case TS_WIND_ORDREL:
			if ((1 << sk->state) & (TCPM_CLOSING))
				return ctrace(t_ordrel_ind(q));
			swerr();
			break;
		case TS_DATA_XFER:
		case TS_WREQ_ORDREL:
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
			swerr();
			break;
		case TS_IDLE:
		case TS_WRES_CIND:
			if ((1 << sk->state) & (TCPM_CONNIND))
				return ctrace(inet_conn_ind(q, mp));
#if 0
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind(q, &ip->dst, T_PROVIDER, 0, mp, NULL));
#endif
			swerr();
			break;
		default:
			swerr();
			break;
		}
		printd(("SWERR: socket state %d in TPI state %d\n", sk->state, ip->state));
		return (-EFAULT);
	case SOCK_DGRAM:
	case SOCK_RAW:
	case SOCK_RDM:
		switch (ip->state) {
		case TS_DATA_XFER:
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind
					   (q, &ip->dst, T_PROVIDER, 0, NULL, NULL));
			swerr();
			break;
		default:
			swerr();
			break;
		}
		printd(("SWERR: socket state %d in TPI state %d\n", sk->state, ip->state));
		return (-EFAULT);
	default:
		swerr();
		printd(("SWERR: unsupported socket type\n"));
		return (-EFAULT);
	}
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_READ Handling
 *
 *  -------------------------------------------------------------------------
 *
 *  On the read queue, an M_READ is used to indicate a data_ready callback
 *  on the socket.  This can mean that the socket is now readable (when it
 *  was blocked before).
 */
STATIC int inet_r_read(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	struct sock *sk = *((struct sock **)mp->b_rptr);

	ensure(ip, return (-EFAULT));
	ensure(sk, return (-EFAULT));
	unless(sk->err, return (-EFAULT));
	switch (ip->type) {
	case SOCK_STREAM:	/* TCP */
	case SOCK_SEQPACKET:	/* SCTP */
		switch (ip->state) {
		case TS_WCON_CREQ:
			if (sk->state == TCP_ESTABLISHED)
				return ctrace(t_conn_con(q, &ip->dst, NULL, NULL));
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
			swerr();
			break;
		case TS_IDLE:
		case TS_WRES_CIND:
			if (sk->state == TCP_LISTEN)
				return ctrace(inet_conn_ind(q, mp));
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
			swerr();
			break;
		case TS_DATA_XFER:
		case TS_WIND_ORDREL:
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
			/* just a normal data indication */
			return (QR_DONE);
		default:
			swerr();
			break;
		}
		printd(("SWERR: socket state %d in TPI state %d\n", sk->state, ip->state));
		return (-EFAULT);
	case SOCK_DGRAM:	/* UDP */
	case SOCK_RAW:		/* RAWIP */
	case SOCK_RDM:		/* RDM (RUDP) */
		switch (ip->state) {
		case TS_DATA_XFER:
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
		case TS_IDLE:
			return (QR_DONE);
		default:
			swerr();
			break;
		}
		printd(("SWERR: socket state %d in TPI state %d\n", sk->state, ip->state));
		return (-EFAULT);
	default:
		swerr();
		break;
	}
	printd(("SWERR: unsupported socket type %d\n", ip->type));
	return (-EFAULT);
}

/*
 *  On the write queue, an M_READ is used to indicate a write_space callback
 *  on the socket.  This can mean that the socket is now writeable (when it
 *  was blocked before).
 */
STATIC int inet_w_read(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);
	struct sock *sk = *((struct sock **)mp->b_rptr);

	ensure(ip, return (-EFAULT));
	ensure(sk, return (-EFAULT));
	unless(sk->err, return (-EFAULT));
	switch (ip->type) {
	case SOCK_STREAM:
	case SOCK_SEQPACKET:
		switch (ip->state) {
		case TS_WCON_CREQ:
			if (sk->state == TCP_ESTABLISHED)
				return ctrace(t_conn_con(q, &ip->dst, NULL, NULL));
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
			swerr();
			break;
		case TS_IDLE:
		case TS_WRES_CIND:
			if (sk->state == TCP_LISTEN)
				return ctrace(inet_conn_ind(q, mp));
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
			swerr();
			break;
		case TS_DATA_XFER:
		case TS_WREQ_ORDREL:
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
			/* just a normal data indication */
			return (QR_DONE);
		default:
			swerr();
			break;
		}
		printd(("SWERR: socket state %d in TPI state %d\n", sk->state, ip->state));
		return (-EFAULT);
	case SOCK_DGRAM:
	case SOCK_RAW:
	case SOCK_RDM:
		switch (ip->state) {
		case TS_DATA_XFER:
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
		case TS_IDLE:
			return (QR_DONE);
		default:
			swerr();
			break;
		}
		printd(("SWERR: socket state %d in TPI state %d\n", sk->state, ip->state));
		return (-EFAULT);
	default:
		swerr();
		break;
	}
	printd(("SWERR: unsupported socket type %d\n", ip->type));
	return (-EFAULT);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 *  This priority STREAMS message is used to signal a socket error callback
 *  event to the STREAMS portion of the INET driver.  We always examine the
 *  state of the socket, the state of the TPI interface, and the socket error
 *  to determine what actions to perform.
 */
STATIC int inet_r_error(queue_t * q, mblk_t * mp)
{
	int err;
	inet_t *ip = INET_PRIV(q);
	struct sock *sk = *((struct sock **)mp->b_rptr);

	ensure(ip, return (-EFAULT));
	ensure(sk, return (-EFAULT));
	err = sock_error(sk);
	ensure(err, return (-EFAULT));
	switch (ip->type) {
	case SOCK_STREAM:
	case SOCK_SEQPACKET:
		switch (ip->state) {
		case TS_IDLE:
		case TS_WRES_CIND:
			if (ip->conind && (1 << sk->state) & (TCPM_CLOSING))
				return ctrace(m_error(q, err));
		case TS_WCON_CREQ:
		case TS_DATA_XFER:
		case TS_WIND_ORDREL:
		case TS_WREQ_ORDREL:
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind
					   (q, NULL, T_PROVIDER, -err, NULL, NULL));
			/* non-critical error */
			ptrace(("INFO: ignoring error event %d\n", err));
			return (QR_DONE);
		default:
			swerr();
			break;
		}
		printd(("SWERR: socket state %d in TPI state %d\n", sk->state, ip->state));
		return (-EFAULT);
	case SOCK_DGRAM:
	case SOCK_RAW:
	case SOCK_RDM:
		switch (ip->state) {
		case TS_IDLE:
			if ((1 << sk->state) & (TCPM_CLOSING))
				return ctrace(m_error(q, err));
			swerr();
			break;
		case TS_DATA_XFER:
			if ((1 << sk->state) & (TCPM_CLOSING))
				return
				    ctrace(t_discon_ind
					   (q, NULL, T_PROVIDER, -err, NULL, NULL));
			swerr();
			break;
		default:
			swerr();
			break;
		}
		printd(("SWERR: socket state %d in TPI state %d\n", sk->state, ip->state));
		return (-EFAULT);
	default:
		swerr();
		break;
	}
	printd(("SWERR: unsupported socket type %d\n", ip->type));
	return (-EFAULT);
}

/* 
 *  =========================================================================
 *
 *  QUEUE PUT and SERVICE routines
 *
 *  =========================================================================
 *
 *  WRITE PUT ad SERVICE (Message from above IP-User --> IP-Provider
 *  -------------------------------------------------------------------------
 */
STATIC int inet_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return inet_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return inet_w_proto(q, mp);
	case M_READ:
		return inet_w_read(q, mp);
	case M_FLUSH:
		return inet_w_flush(q, mp);
		// case M_IOCTL: return inet_w_ioctl (q, mp);
	}
	return (-EOPNOTSUPP);
}

/* 
 *  READ PUT ad SERVICE (Message from below IP-Provider --> IP-User
 *  -------------------------------------------------------------------------
 */
STATIC int inet_r_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_PCRSE:
		return inet_r_pcrse(q, mp);
	case M_READ:
		return inet_r_read(q, mp);
	case M_ERROR:
		return inet_r_error(q, mp);
	case M_FLUSH:
		return inet_r_flush(q, mp);
	}
	return (QR_PASSFLOW);
}

/* 
 *  =========================================================================
 *
 *  QUEUE PUT and SERVICE routines
 *
 *  =========================================================================
 *
 *  PUTQ Put Routine
 *  -----------------------------------
 */
STATIC INLINE int inet_putq(queue_t * q, mblk_t * mp, int (*proc) (queue_t *, mblk_t *))
{
	int rtn = 0;

	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	if (mp->b_datap->db_type < QPCTL) {
		putq(q, mp);
		return (0);
	}
	if (!inet_trylock(q)) {
		switch ((rtn = (*proc) (q, mp))) {
		case QR_DONE:
			freemsg(mp);
		case QR_ABSORBED:
			break;
		case QR_TRIMMED:
			freeb(mp);
			break;
		case QR_LOOP:
			if (!q->q_next) {
				qreply(q, mp);
				break;
			}
		case QR_PASSALONG:
			if (q->q_next) {
				putnext(q, mp);
				break;
			}
			rtn = -EOPNOTSUPP;
		default:
			ptrace(("ERROR: (dropping) %d\n", rtn));
			freemsg(mp);
			break;
		case QR_DISABLE:
			putq(q, mp);
			rtn = 0;
			break;
		case QR_PASSFLOW:
			if (mp->b_datap->db_type >= QPCTL || canputnext(q)) {
				putnext(q, mp);
				break;
			}
		case -ENOBUFS:	/* caller must schedule bufcall */
		case -EBUSY:	/* caller must have failed canput */
		case -EAGAIN:	/* caller must re-enable queue */
		case -ENOMEM:	/* caller must re-enable queue */
			putq(q, mp);
			break;
		}
		inet_unlock(q);
	} else {
		rare();
		putq(q, mp);
	}
	return (rtn);
}

/* 
 *  SRVQ Service Routine
 *  -----------------------------------
 */
STATIC INLINE int inet_srvq(queue_t * q, int (*proc) (queue_t *, mblk_t *))
{
	int rtn = 0;
	mblk_t *mp;

	ensure(q, return (-EFAULT));
	if (!inet_trylock(q)) {
		while ((mp = getq(q))) {
			switch ((rtn = (*proc) (q, mp))) {
			case QR_DONE:
				freemsg(mp);
			case QR_ABSORBED:
				continue;
			case QR_TRIMMED:
				freeb(mp);
				continue;
			case QR_LOOP:
				if (!q->q_next) {
					qreply(q, mp);
					continue;
				}
			case QR_PASSALONG:
				if (q->q_next) {
					putnext(q, mp);
					continue;
				}
				rtn = -EOPNOTSUPP;
			default:
				ptrace(("ERROR: (dropping) %d\n", rtn));
				freemsg(mp);
				continue;
			case QR_DISABLE:
				ptrace(("ERROR: Noenabling queue\n"));
				noenable(q);
				putbq(q, mp);
				rtn = 0;
				break;
			case QR_PASSFLOW:
				if (mp->b_datap->db_type >= QPCTL || canputnext(q)) {
					putnext(q, mp);
					continue;
				}
			case -ENOBUFS:	/* caller must schedule bufcall */
			case -EBUSY:	/* caller must have failed canput */
			case -EAGAIN:	/* caller must re-enable queue */
			case -ENOMEM:	/* caller must re-enable queue */
				if (mp->b_datap->db_type < QPCTL) {
					ptrace(("ERROR: Puting back, queue stalled\n"));
					putbq(q, mp);
					break;
				}
				if (mp->b_datap->db_type == M_PCPROTO) {
					mp->b_datap->db_type = M_PROTO;
					mp->b_band = 255;
					putq(q, mp);
					break;
				}
				ptrace(("ERROR: (dropping) %d\n", rtn));
				freemsg(mp);
				continue;
			}
			break;
		}
		inet_unlock(q);
	} else {
		rare();
		qenable(q);
	}
	return (rtn);
}
STATIC INT inet_rput(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);

	ensure(ip, return ((INT) (-EFAULT)));
	ensure(ip->ops.r_prim, return ((INT) (-EFAULT)));
	return (INT) inet_putq(q, mp, ip->ops.r_prim);
}
STATIC INT inet_rsrv(queue_t * q)
{
	inet_t *ip = INET_PRIV(q);

	ensure(ip, return ((INT) (-EFAULT)));
	ensure(ip->ops.r_prim, return ((INT) (-EFAULT)));
	return (INT) inet_srvq(q, ip->ops.r_prim);
}
STATIC INT inet_wput(queue_t * q, mblk_t * mp)
{
	inet_t *ip = INET_PRIV(q);

	ensure(ip, return ((INT) (-EFAULT)));
	ensure(ip->ops.w_prim, return ((INT) (-EFAULT)));
	return (INT) inet_putq(q, mp, ip->ops.w_prim);
}
STATIC INT inet_wsrv(queue_t * q)
{
	inet_t *ip = INET_PRIV(q);

	ensure(ip, return ((INT) (-EFAULT)));
	ensure(ip->ops.w_prim, return ((INT) (-EFAULT)));
	return (INT) inet_srvq(q, ip->ops.w_prim);
}

/* 
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 *
 *  OPEN
 *  -------------------------------------------------------------------------
 */
typedef struct inet_profile {
	uint family;
	uint type;
	uint proto;
	size_t tsdu;
	size_t etsdu;
	size_t cdata;
	size_t ddata;
	size_t asize;
	size_t osize;
	size_t tidu;
	uint serv_type;
	uint prov_flags;
} inet_profile_t;
STATIC const inet_profile_t inet_profiles[] = {
	{
	 PF_INET, SOCK_RAW, IPPROTO_ICMP,
	 0xffff, T_INVALID, T_INVALID, T_INVALID,
	 sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_COTS | T_CLTS,
	 XPG4_1 & ~T_SNDZERO},
	{
	 PF_INET, SOCK_RAW, IPPROTO_IGMP,
	 0xffff, T_INVALID, T_INVALID, T_INVALID,
	 sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_COTS | T_CLTS,
	 XPG4_1 & ~T_SNDZERO},
	{
	 PF_INET, SOCK_RAW, IPPROTO_IPIP, 0xffff,
	 T_INVALID, T_INVALID, T_INVALID,
	 sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_COTS | T_CLTS,
	 XPG4_1 & ~T_SNDZERO},
	{
	 PF_INET, SOCK_STREAM, IPPROTO_TCP,
	 T_INVALID, 1, T_INVALID, T_INVALID,
	 sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_COTS_ORD,
	 XPG4_1 & ~T_SNDZERO},
	{
	 PF_INET, SOCK_RAW, IPPROTO_EGP,
	 0xffff, T_INVALID, T_INVALID, T_INVALID,
	 sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_COTS | T_CLTS,
	 XPG4_1 & ~T_SNDZERO},
	{
	 PF_INET, SOCK_RAW, IPPROTO_PUP,
	 0xffff, T_INVALID, T_INVALID, T_INVALID,
	 sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_COTS | T_CLTS,
	 XPG4_1 & ~T_SNDZERO},
	{
	 PF_INET, SOCK_DGRAM, IPPROTO_UDP,
	 0xffff, T_INVALID, T_INVALID, T_INVALID,
	 sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_COTS | T_CLTS,
	 XPG4_1 & ~T_SNDZERO},
	{
	 PF_INET, SOCK_RAW, IPPROTO_IDP,
	 0xffff, T_INVALID, T_INVALID, T_INVALID,
	 sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_COTS | T_CLTS,
	 XPG4_1 & ~T_SNDZERO},
	{
	 PF_INET, SOCK_RAW, IPPROTO_RAW,
	 0xffff, T_INVALID, T_INVALID, T_INVALID,
	 sizeof(struct sockaddr_in), T_INFINITE, 0xffff, T_COTS | T_CLTS,
	 XPG4_1 & ~T_SNDZERO}
};
STATIC int inet_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * cred)
{
	inet_t *ip, **ipp;
	int cmajor = getmajor(*devp);
	int cminor = getminor(*devp);
	int bminor = cminor;
	const inet_profile_t *prof = NULL;

	(void)inet_dflt_dest;
	(void)inet_dflt_lstn;
	if (sflag == MODOPEN || WR(q)->q_next) {
		ptrace(("ERROR: Can't push as module\n"));
		return (EIO);
	}
	if (q->q_ptr != NULL) {
		ptrace(("INFO: Device already open\n"));
		return (0);
	}
	if (sflag == CLONEOPEN) {
		ptrace(("INFO: Clone open in effect\n"));
		cmajor = INET_CMAJOR;
		cminor = 1;
	}
	if (cmajor == INET_CMAJOR) {
		if (bminor >= ICMP_CMINOR && bminor <= RAWIP_CMINOR) {
			ptrace(("INFO: Clone minor opened\n"));
			prof = &inet_profiles[bminor - ICMP_CMINOR];
			sflag = CLONEOPEN;
			cminor = FREE_CMINOR;
		} else
			return (ENXIO);
	}
	for (ipp = &inet_opens; *ipp; ipp = &(*ipp)->next) {
		ushort dmajor = getmajor((*ipp)->devid);

		if (cmajor < dmajor)
			break;
		if (cmajor == dmajor) {
			ushort dminor = getminor((*ipp)->devid);

			if (cminor < dminor)
				break;
			if (cminor == dminor) {
				if (sflag == CLONEOPEN) {
					if (++cminor >= INET_NMINOR) {
						if (++cmajor >= INET_CMAJOR + INET_NMAJOR)
							break;
						cminor = 0;
					}
					continue;
				}
				ptrace(("ERROR: Requested device (%d,%d) in use\n", cmajor,
					cminor));
				return (EIO);
			}
		}
	}
	if (cmajor >= INET_CMAJOR + INET_NMAJOR) {
		ptrace(("ERROR: No devices available\n"));
		return (ENXIO);
	}
	MOD_INC_USE_COUNT;
	if (!(ip = kmalloc(sizeof(*ip), GFP_KERNEL))) {
		ptrace(("ERROR: No memory\n"));
		MOD_DEC_USE_COUNT;
		return (ENOMEM);
	}
	*devp = makedevice(cmajor, cminor);
	ptrace(("INFO: Making device major %d, minor %d\n", cmajor, cminor));
	bzero(ip, sizeof(*ip));
	RD(q)->q_ptr = WR(q)->q_ptr = ip;
	inet_init_lock(q);
	ip->rq = RD(q);
	ip->wq = WR(q);
	ip->devid = *devp;
	ip->cred = *cred;
	ip->state = 0;
	ip->ops.r_prim = &inet_r_prim;
	ip->ops.w_prim = &inet_w_prim;
	ip->family = prof->family;
	ip->type = prof->type;
	ip->proto = prof->proto;
	ip->tsdu = prof->tsdu;
	ip->etsdu = prof->etsdu;
	ip->cdata = prof->cdata;
	ip->ddata = prof->ddata;
	ip->asize = prof->asize;
	ip->osize = prof->osize;
	ip->tidu = prof->tidu;
	ip->serv_type = prof->serv_type;
	ip->prov_flags = prof->prov_flags;
	if ((ip->next = *ipp))
		ip->next->prev = &ip->next;
	ip->prev = ipp;
	*ipp = ip;
	return (0);
}

/* 
 *  CLOSE
 *  -------------------------------------------------------------------------
 */
STATIC int inet_close(queue_t * q, int flag, cred_t * crp)
{
	mblk_t *cp;
	inet_t *ip = INET_PRIV(q);

	if (!ip)
		return (EIO);
	while ((cp = bufq_dequeue(&ip->conq))) {
		struct sock *sk = *((struct sock **)cp->b_rptr);

		freemsg(cp);
		if (sk->socket)
			inet_socket_put(sk->socket);
	}
	if (ip->sock)
		inet_socket_put(xchg(&ip->sock, NULL));
	if (ip->rbid)
		unbufcall(xchg(&ip->rbid, 0));
	if (ip->wbid)
		unbufcall(xchg(&ip->wbid, 0));
	if ((*(ip->prev) = ip->next))
		ip->next->prev = ip->prev;
	WR(q)->q_ptr = RD(q)->q_ptr = NULL;
	kfree(ip);
	MOD_DEC_USE_COUNT;
	return (0);
}

/* 
 *  =========================================================================
 *
 *  LiS Module Initialization
 *
 *  =========================================================================
 */
STATIC void inet_init(void)
{
	int cmajor;

	if ((cmajor =
	     lis_register_strdev(INET_CMAJOR, &inet_info, INET_NMINOR,
				 inet_minfo.mi_idname)) < 0) {
		inet_minfo.mi_idnum = 0;
		rare();
		cmn_err(CE_NOTE, "ip: couldn't register T-driver\n");
		return;
	}
	inet_minfo.mi_idnum = cmajor;
}
STATIC void inet_term(void)
{
	if (inet_minfo.mi_idnum) {
		if ((inet_minfo.mi_idnum = lis_unregister_strdev(inet_minfo.mi_idnum))) {
			inet_minfo.mi_idnum = 0;
			rare();
			cmn_err(CE_WARN, "ip: couldn't unregsiter T-driver!\n");
		}
	}
}

/* 
 *  =========================================================================
 *
 *  LINUX MODULE INITIALIZATION
 *
 *  =========================================================================
 */
int init_module(void)
{
	cmn_err(CE_NOTE, INET_BANNER);	/* console splash */
	inet_init();
	return (0);
}
void cleanup_module(void)
{
	inet_term();
	return;
}
