OpenSS7
SS7 for the
Common Man

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified:

Home Overview Status News Documentation Resources About
   
 Overview
 Status
 News
 Documentation
 Resources
 About

   
Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/inet/inet.c


File /code/strss7/drivers/inet/inet.c



#ident "@(#) $RCSfile: inet.c,v $ $Name:  $($Revision: 0.8.2.27 $) $Date: 2003/05/27 10:08:28 $"

static char const ident[] = "$RCSfile: inet.c,v $ $Name:  $($Revision: 0.8.2.27 $) $Date: 2003/05/27 10:08:28 $";

/*
 *  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 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 <net/tcp.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>

#if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE)
#include <netinet/sctp.h>
#endif

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

#define	SS_DESCRIP	"SOCKSYS STREAMS (TPI) DRIVER." "\n" \
			"Part of the OpenSS7 Stack for LiS STREAMS."
#define SS_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  All Rights Reserved."
#define SS_DEVICE	"Supports OpenSS7 INET Drivers."
#define SS_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define SS_LICENSE	"GPL"
#define SS_BANNER	SS_DESCRIP	"\n" \
			SS_COPYRIGHT	"\n" \
			SS_DEVICE	"\n" \
			SS_CONTACT

MODULE_AUTHOR(SS_CONTACT);
MODULE_DESCRIPTION(SS_DESCRIP);
MODULE_SUPPORTED_DEVICE(SS_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(SS_LICENSE);
#endif

#ifndef SS_CMAJOR
#define SS_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 SS_NMAJOR
#define SS_NMAJOR 4
#define SS_NMINOR 256
#endif

#ifndef SS_IOC_MAGIC
#define SS_IOC_MAGIC 'i'
#endif

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

/*
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */
#define SS_MOD_NAME	"socksys"
#define SS_MOD_ID	('I'<<8|SS_IOC_MAGIC)

STATIC struct module_info ss_minfo = {
	mi_idnum:SS_MOD_ID,			/* Module ID number */
	mi_idname:SS_MOD_NAME,			/* 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 ss_open(queue_t *, dev_t *, int, int, cred_t *);
STATIC int ss_close(queue_t *, int, cred_t *);

STATIC INT ss_rput(queue_t *, mblk_t *);
STATIC INT ss_rsrv(queue_t *);

STATIC struct qinit ss_rinit = {
	qi_putp:ss_rput,			/* Read put (msg from below) */
	qi_srvp:ss_rsrv,			/* Read queue service */
	qi_qopen:ss_open,			/* Each open */
	qi_qclose:ss_close,			/* Last close */
	qi_minfo:&ss_minfo,			/* Information */
};

STATIC INT ss_wput(queue_t *, mblk_t *);
STATIC INT ss_wsrv(queue_t *);

STATIC struct qinit ss_winit = {
	qi_putp:ss_wput,			/* Write put (msg from above) */
	qi_srvp:ss_wsrv,			/* Write queue service */
	qi_minfo:&ss_minfo,			/* Information */
};

STATIC struct streamtab ss_info = {
	st_rdinit:&ss_rinit,			/* Upper read queue */
	st_wrinit:&ss_winit,			/* Lower read 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
#define QR_STRIP	7

/*
 *  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 ss_addr_t;

typedef struct ss_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, T_SCTP_NODELAY */
	ulong mss;				/* T_TCP_MAXSEG */
	ulong alive;				/* T_TCP_KEEPALIVE */
	ulong csum;				/* T_UDP_CHECKSUM */
#if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE)
	ulong cork;				/* T_SCTP_CORK */
	ulong ppi;				/* T_SCTP_PPI */
	ulong sid;				/* T_SCTP_SID */
	ulong ssn;				/* T_SCTP_SSN */
	ulong tsn;				/* T_SCTP_TSN */
	ulong recvopt;				/* T_SCTP_RECVOPT */
	ulong clife;				/* T_SCTP_COOKIE_LIFE */
	ulong sack;				/* T_SCTP_SACK_DELAY */
	ulong prtx;				/* T_SCTP_PATH_MAX_RETRANS */
	ulong artx;				/* T_SCTP_ASSOC_MAX_RETRANS */
	ulong irtx;				/* T_SCTP_MAX_INIT_RETRIES */
	ulong hitvl;				/* T_SCTP_HEARTBEAT_ITVL */
	ulong rtoinit;				/* T_SCTP_RTO_INITIAL */
	ulong rtomin;				/* T_SCTP_RTO_MIN */
	ulong rtomax;				/* T_SCTP_RTO_MAX */
	ulong ostr;				/* T_SCTP_OSTREAMS */
	ulong istr;				/* T_SCTP_ISTREAMS */
	ulong cinc;				/* T_SCTP_COOKIE_INC */
	ulong titvl;				/* T_SCTP_THROTTLE_ITVL */
	ulong mac;				/* T_SCTP_MAC_TYPE */
#endif
} ss_dflt_t;

typedef struct ss_profile {
	struct {
		uint family;
		uint type;
		uint protocol;
	} prot;
	struct T_info_ack info;
} ss_profile_t;

typedef struct inet {
	struct inet *next;			/* list of all IP-Users */
	struct inet **prev;			/* list of all IP-Users */
	size_t refcnt;				/* structure reference count */
	lis_spin_lock_t lock;			/* structure lock */
	ushort cmajor;				/* major device number */
	ushort cminor;				/* minor device number */
	queue_t *rq;				/* associated read queue */
	queue_t *wq;				/* associated write queue */
	cred_t cred;				/* credientials */
	lis_spin_lock_t qlock;			/* queue lock */
	queue_t *rwait;				/* RD queue waiting on lock */
	queue_t *wwait;				/* WR queue waiting on lock */
	uint rbid;				/* RD buffer call id */
	uint wbid;				/* WR buffer call id */
	ushort port;				/* port/protocol number */
	int tcp_state;				/* tcp state history */
	ss_profile_t p;				/* protocol profile */
	struct {
		void (*state_change) (struct sock *);
		void (*data_ready) (struct sock *, int);
		void (*write_space) (struct sock *);
		void (*error_report) (struct sock *);
	} cb_save;				/* call back holder */
	ss_addr_t src;				/* bound address */
	ss_addr_t dst;				/* connected address */
	ss_dflt_t options;			/* protocol options */
	unsigned char _pad[40];			/* pad for options */
	bufq_t conq;				/* connection queue */
	uint conind;				/* number of connection indications */
	struct socket *sock;			/* socket pointer */
} ss_t;

#define PRIV(__q) ((ss_t *)((__q)->q_ptr))
#define SOCK_PRIV(__sk) ((ss_t *)(__sk)->user_data)

typedef struct ss_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, T_SCTP_NODELAY */
	ulong *mss;				/* T_TCP_MAXSEG */
	ulong *alive;				/* T_TCP_KEEPALIVE */
	ulong *csum;				/* T_UDP_CHECKSUM */
	ulong *ropt;
	struct in_pktinfo *pkinfo;
#if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE)
	ulong *cork;				/* T_SCTP_CORK */
	ulong *ppi;				/* T_SCTP_PPI */
	ulong *sid;				/* T_SCTP_SID */
	ulong *ssn;				/* T_SCTP_SSN */
	ulong *tsn;				/* T_SCTP_TSN */
	ulong *recvopt;				/* T_SCTP_RECVOPT */
	ulong *clife;				/* T_SCTP_COOKIE_LIFE */
	ulong *sack;				/* T_SCTP_SACK_DELAY */
	ulong *prtx;				/* T_SCTP_PATH_MAX_RETRANS */
	ulong *artx;				/* T_SCTP_ASSOC_MAX_RETRANS */
	ulong *irtx;				/* T_SCTP_MAX_INIT_RETRIES */
	ulong *hitvl;				/* T_SCTP_HEARTBEAT_ITVL */
	ulong *rtoinit;				/* T_SCTP_RTO_INITIAL */
	ulong *rtomin;				/* T_SCTP_RTO_MIN */
	ulong *rtomax;				/* T_SCTP_RTO_MAX */
	ulong *ostr;				/* T_SCTP_OSTREAMS */
	ulong *istr;				/* T_SCTP_ISTREAMS */
	ulong *cinc;				/* T_SCTP_COOKIE_INC */
	ulong *titvl;				/* T_SCTP_THROTTLE_ITVL */
	ulong *mac;				/* T_SCTP_MAC_TYPE */
	struct t_sctp_hb *hb;			/* T_SCTP_HB */
	struct t_sctp_rto *rto;			/* T_SCTP_RTO */
	struct t_sctp_status *stat;		/* T_SCTP_STATUS */
	ulong *debug;				/* T_SCTP_DEBUG */
#endif
} ss_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			(1<< 0)
#define TF_IP_TOS			(1<< 1)
#define TF_IP_TTL			(1<< 2)
#define TF_IP_REUSEADDR			(1<< 3)
#define TF_IP_DONTROUTE			(1<< 4)
#define TF_IP_BROADCAST			(1<< 5)
#define TF_IP_PKTINFO			(1<< 6)
#define TF_IP_RETOPTS			(1<< 7)

#define TF_TCP_NODELAY			(1<< 8)
#define TF_TCP_MAXSEG			(1<< 9)
#define TF_TCP_KEEPALIVE		(1<<10)
#define TF_UDP_CHECKSUM			(1<<11)

#if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE)
#define TF_SCTP_NODELAY			TF_TCP_NODELAY
#define TF_SCTP_MAXSEG			TF_TCP_MAXSEG
#define TF_SCTP_CORK			(1<<10)
#define TF_SCTP_PPI			(1<<11)
#define TF_SCTP_SID			(1<<12)
#define TF_SCTP_SSN			(1<<13)
#define TF_SCTP_TSN			(1<<14)
#define TF_SCTP_RECVOPT			(1<<15)
#define TF_SCTP_COOKIE_LIFE		(1<<16)
#define TF_SCTP_SACK_DELAY		(1<<17)
#define TF_SCTP_MAX_RETRANS		(1<<18)
#define TF_SCTP_ASSOC_MAX_RETRANS	(1<<19)
#define TF_SCTP_MAX_INIT_RETRIES	(1<<20)
#define TF_SCTP_HEARTBEAT_ITVL		(1<<21)
#define TF_SCTP_RTO_INITIAL		(1<<22)
#define TF_SCTP_RTO_MIN			(1<<23)
#define TF_SCTP_RTO_MAX			(1<<24)
#define TF_SCTP_OSTREAMS		(1<<25)
#define TF_SCTP_ISTREAMS		(1<<26)
#define TF_SCTP_COOKIE_INC		(1<<27)
#define TF_SCTP_THROTTLE_ITVL		(1<<28)
#define TF_SCTP_MAC_TYPE		(1<<29)
#define TF_SCTP_HB			(1<<30)
#define TF_SCTP_RTO			(1<<31)
#define TF_SCTP_STATUS			(1<< 6)	/* FIXME */
#define TF_SCTP_DEBUG			(1<< 7)	/* FIXME */
#endif

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

typedef struct ss_event {
	struct sock *sk;			/* sock (child) for event */
	int state;				/* state at time of event */
} ss_event_t;

STATIC lis_spin_lock_t ss_lock;			/* protects ss_opens lists */
STATIC ss_t *ss_opens = NULL;

#if 0

/*
 * for later when we support default destinations and default listeners 
 */
STATIC ss_t *ss_dflt_dest = NULL;
STATIC ss_t *ss_dflt_lstn = NULL;
#endif

/*
 *  =========================================================================
 *
 *  Socket Callbacks
 *
 *  =========================================================================
 */
STATIC void ss_state_change(struct sock *sk);
STATIC void ss_write_space(struct sock *sk);
STATIC void ss_error_report(struct sock *sk);
STATIC void ss_data_ready(struct sock *sk, int len);
STATIC void ss_socket_put(struct socket *sock)
{
	struct sock *sk;
	ensure(sock, return);
	if ((sk = sock->sk)) {
		write_lock(&sk->callback_lock);
		{
			ss_t *ss;
			if ((ss = SOCK_PRIV(sk))) {
				SOCK_PRIV(sk) = NULL;
				ss->refcnt--;
				sk->state_change = ss->cb_save.state_change;
				sk->data_ready = ss->cb_save.data_ready;
				sk->write_space = ss->cb_save.write_space;
				sk->error_report = ss->cb_save.error_report;
			} else
				assure(ss);
		}
		write_unlock(&sock->sk->callback_lock);
	} else
		assure(sk);
	sock_release(sock);
}
STATIC void ss_socket_get(struct socket *sock, ss_t * ss)
{
	struct sock *sk;
	ensure(sock, return);
	sk = sock->sk;
	ensure(sk, return);
	write_lock(&sock->sk->callback_lock);
	{
		SOCK_PRIV(sk) = ss;
		ss->refcnt++;
		ss->cb_save.state_change = sk->state_change;
		ss->cb_save.data_ready = sk->data_ready;
		ss->cb_save.write_space = sk->write_space;
		ss->cb_save.error_report = sk->error_report;
		sk->state_change = ss_state_change;
		sk->data_ready = ss_data_ready;
		sk->write_space = ss_write_space;
		sk->error_report = ss_error_report;
#ifdef LINUX_2_4
		sk->protinfo.af_inet.recverr = 1;
#else
		sk->ip_recverr = 1;
#endif
		ss->tcp_state = sk->state;	/* initialized tcp state */
	}
	write_unlock(&sock->sk->callback_lock);
}

/*
 *  =========================================================================
 *
 *  Locking
 *
 *  =========================================================================
 */
STATIC int ss_trylockq(queue_t * q)
{
	int res;
	ss_t *ss = PRIV(q);
	if (!(res = lis_spin_trylock(&ss->qlock))) {
		if (q == ss->rq)
			ss->rwait = q;
		if (q == ss->wq)
			ss->wwait = q;
	}
	return (res);
}
STATIC void ss_unlockq(queue_t * q)
{
	ss_t *ss = PRIV(q);
	lis_spin_unlock(&ss->qlock);
	if (ss->rwait)
		qenable(xchg(&ss->rwait, NULL));
	if (ss->wwait)
		qenable(xchg(&ss->wwait, NULL));
}

/*
 *  =========================================================================
 *
 *  Buffer Allocation
 *
 *  =========================================================================
 */
/*
 *  BUFSRV calls service routine
 *  -------------------------------------------------------------------------
 */
STATIC void ss_bufsrv(long data)
{
	queue_t *q = (queue_t *) data;
	if (q) {
		ss_t *ss = PRIV(q);
		if (q == ss->rq)
			if (ss->rbid) {
				ss->rbid = 0;
				ss->refcnt--;
			}
		if (q == ss->wq)
			if (ss->wbid) {
				ss->wbid = 0;
				ss->refcnt--;
			}
		qenable(q);
	}
}

/*
 *  UNBUFCALL
 *  -------------------------------------------------------------------------
 */
STATIC void ss_unbufcall(queue_t * q)
{
	ss_t *ss = PRIV(q);
	if (ss->rbid) {
		unbufcall(xchg(&ss->rbid, 0));
		ss->refcnt--;
	}
	if (ss->wbid) {
		unbufcall(xchg(&ss->wbid, 0));
		ss->refcnt--;
	}
}

/*
 *  ALLOCB
 *  -------------------------------------------------------------------------
 */
STATIC mblk_t *ss_allocb(queue_t * q, size_t size, int prior)
{
	mblk_t *mp;
	if ((mp = allocb(size, prior)))
		return (mp);
	else {
		ss_t *ss = PRIV(q);
		if (q == ss->rq) {
			if (!ss->rbid) {
				ss->rbid = bufcall(size, prior, &ss_bufsrv, (long) q);
				ss->refcnt++;
			}
			return (NULL);
		}
		if (q == ss->wq) {
			if (!ss->wbid) {
				ss->wbid = bufcall(size, prior, &ss_bufsrv, (long) q);
				ss->refcnt++;
			}
			return (NULL);
		}
	}
	swerr();
	return (NULL);
}

#if 0
/*
 *  ESBALLOC
 *  -------------------------------------------------------------------------
 */
STATIC mblk_t *ss_esballoc(queue_t * q, unsigned char *base, size_t size, int prior, frtn_t * frtn)
{
	mblk_t *mp;
	if ((mp = esballoc(base, size, prior, frtn)))
		return (mp);
	else {
		ss_t *ss = PRIV(q);
		if (q == ss->rq) {
			if (!ss->rbid) {
				ss->rbid = esbbcall(prior, &ss_bufsrv, (long) q);
				ss->refcnt++;
			}
			return (NULL);
		}
		if (q == ss->wq) {
			if (!ss->wbid) {
				ss->wbid = esbbcall(prior, &ss_bufsrv, (long) q);
				ss->refcnt++;
			}
			return (NULL);
		}
		swerr();
		return (NULL);
	}
}
#endif

/*
 *  =========================================================================
 *
 *  OPTION Handling
 *
 *  =========================================================================
 */
#define _T_ALIGN_SIZEOF(s) \
	((sizeof((s)) + _T_ALIGN_SIZE - 1) & ~(_T_ALIGN_SIZE - 1))
STATIC size_t ss_opts_size(ss_t * ss, ss_opts_t * ops)
{
	size_t len = 0;
	const size_t hlen = sizeof(struct t_opthdr);	/* 32 bytes */
	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 (ss->p.prot.protocol == T_INET_TCP) {
			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 (ss->p.prot.protocol == T_INET_UDP) {
			if (ops->csum)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->csum));
		}
#if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE)
		if (ss->p.prot.protocol == T_INET_SCTP) {
			if (ops->nodly)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->nodly));
			if (ops->cork)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->cork));
			if (ops->mss)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->mss));
			if (ops->ppi)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->ppi));
			if (ops->sid)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->sid));
			if (ops->ssn)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->ssn));
			if (ops->tsn)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->tsn));
			if (ops->recvopt)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->recvopt));
			if (ops->clife)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->clife));
			if (ops->sack)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->sack));
			if (ops->prtx)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->prtx));
			if (ops->artx)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->artx));
			if (ops->irtx)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->irtx));
			if (ops->hitvl)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->hitvl));
			if (ops->rtoinit)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->rtoinit));
			if (ops->rtomin)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->rtomin));
			if (ops->rtomax)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->rtomax));
			if (ops->ostr)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->ostr));
			if (ops->istr)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->istr));
			if (ops->cinc)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->cinc));
			if (ops->titvl)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->titvl));
			if (ops->mac)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->mac));
			if (ops->hb)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->hb));
			if (ops->rto)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->rto));
			if (ops->stat)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->stat));	/* FIXME add more */
			if (ops->debug)
				len += hlen + _T_ALIGN_SIZEOF(*(ops->debug));
		}
#endif
	}
	return (len);
}
STATIC void ss_build_opts(ss_t * ss, ss_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) {
		if (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->flags & TF_IP_OPTIONS) ? T_SUCCESS : T_FAILURE;
			*((struct ip_options *) *p) = *(ops->opt);
			*p += _T_ALIGN_SIZEOF(*ops->opt);
		}
		if (ops->tos) {
			oh = ((typeof(oh)) * p)++;
			oh->len = olen;
			oh->level = T_INET_IP;
			oh->name = T_IP_TOS;
			oh->status = (ops->flags & TF_IP_TOS) ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p) = *(ops->tos);
			*p += _T_ALIGN_SIZEOF(*ops->tos);
		}
		if (ops->ttl) {
			oh = ((typeof(oh)) * p)++;
			oh->len = olen;
			oh->level = T_INET_IP;
			oh->name = T_IP_TTL;
			oh->status = (ops->flags & TF_IP_TTL) ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p) = *(ops->ttl);
			*p += _T_ALIGN_SIZEOF(*ops->ttl);
		}
		if (ops->reuse) {
			oh = ((typeof(oh)) * p)++;
			oh->len = olen;
			oh->level = T_INET_IP;
			oh->name = T_IP_REUSEADDR;
			oh->status = (ops->flags & TF_IP_REUSEADDR) ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p) = *(ops->reuse);
			*p += _T_ALIGN_SIZEOF(*ops->reuse);
		}
		if (ops->norte) {
			oh = ((typeof(oh)) * p)++;
			oh->len = olen;
			oh->level = T_INET_IP;
			oh->name = T_IP_DONTROUTE;
			oh->status = (ops->flags & TF_IP_DONTROUTE) ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p) = *(ops->norte);
			*p += _T_ALIGN_SIZEOF(*ops->norte);
		}
		if (ops->bcast) {
			oh = ((typeof(oh)) * p)++;
			oh->len = olen;
			oh->level = T_INET_IP;
			oh->name = T_IP_BROADCAST;
			oh->status = (ops->flags & TF_IP_BROADCAST) ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p) = *(ops->bcast);
			*p += _T_ALIGN_SIZEOF(*ops->bcast);
		}
		if (ss->p.prot.protocol == T_INET_TCP) {
			if (ops->nodly) {
				oh = ((typeof(oh)) * p)++;
				oh->len = olen;
				oh->level = T_INET_TCP;
				oh->name = T_TCP_NODELAY;
				oh->status = (ops->flags & TF_TCP_NODELAY) ? T_SUCCESS : T_FAILURE;
				*((t_scalar_t *) * p) = *(ops->nodly);
				*p += _T_ALIGN_SIZEOF(*ops->nodly);
			}
			if (ops->mss) {
				oh = ((typeof(oh)) * p)++;
				oh->len = olen;
				oh->level = T_INET_TCP;
				oh->name = T_TCP_MAXSEG;
				oh->status = (ops->flags & TF_TCP_MAXSEG) ? T_SUCCESS : T_FAILURE;
				*((t_scalar_t *) * p) = *(ops->mss);
				*p += _T_ALIGN_SIZEOF(*ops->mss);
			}
			if (ops->alive) {
				oh = ((typeof(oh)) * p)++;
				oh->len = olen;
				oh->level = T_INET_TCP;
				oh->name = T_TCP_KEEPALIVE;
				oh->status = (ops->flags & TF_TCP_KEEPALIVE) ? T_SUCCESS : T_FAILURE;
				*((t_scalar_t *) * p) = *(ops->alive);
				*p += _T_ALIGN_SIZEOF(*ops->alive);
			}
		}
		if (ss->p.prot.protocol == T_INET_UDP) {
			if (ops->csum) {
				oh = ((typeof(oh)) * p)++;
				oh->len = olen;
				oh->level = T_INET_UDP;
				oh->name = T_UDP_CHECKSUM;
				oh->status = (ops->flags & TF_UDP_CHECKSUM) ? T_SUCCESS : T_FAILURE;
				*((t_scalar_t *) * p) = *(ops->csum);
				*p += _T_ALIGN_SIZEOF(*ops->csum);
			}
		}
	}
}
STATIC int ss_parse_opts(ss_t * ss, ss_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 (ss->p.prot.protocol == 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 (ss->p.prot.protocol == 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;
#if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE)
		case T_INET_SCTP:
			if (ss->p.prot.protocol == T_INET_SCTP)
				switch (oh->name) {
				case T_SCTP_NODELAY:
					ops->nodly = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_NODELAY;
					continue;
				case T_SCTP_CORK:
					ops->cork = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_CORK;
					continue;
				case T_SCTP_PPI:
					ops->ppi = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_PPI;
					continue;
				case T_SCTP_SID:
					ops->sid = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_SID;
					continue;
				case T_SCTP_SSN:
					ops->ssn = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_SSN;
					continue;
				case T_SCTP_TSN:
					ops->tsn = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_TSN;
					continue;
				case T_SCTP_RECVOPT:
					ops->recvopt = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_RECVOPT;
					continue;
				case T_SCTP_COOKIE_LIFE:
					ops->clife = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_COOKIE_LIFE;
					continue;
				case T_SCTP_SACK_DELAY:
					ops->sack = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_SACK_DELAY;
					continue;
				case T_SCTP_PATH_MAX_RETRANS:
					ops->prtx = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_MAX_RETRANS;
					continue;
				case T_SCTP_ASSOC_MAX_RETRANS:
					ops->artx = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_ASSOC_MAX_RETRANS;
					continue;
				case T_SCTP_MAX_INIT_RETRIES:
					ops->irtx = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_MAX_INIT_RETRIES;
					continue;
				case T_SCTP_HEARTBEAT_ITVL:
					ops->hitvl = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_HEARTBEAT_ITVL;
					continue;
				case T_SCTP_RTO_INITIAL:
					ops->rtoinit = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_RTO_INITIAL;
					continue;
				case T_SCTP_RTO_MIN:
					ops->rtomin = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_RTO_MIN;
					continue;
				case T_SCTP_RTO_MAX:
					ops->rtomax = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_RTO_MAX;
					continue;
				case T_SCTP_OSTREAMS:
					ops->ostr = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_OSTREAMS;
					continue;
				case T_SCTP_ISTREAMS:
					ops->istr = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_ISTREAMS;
					continue;
				case T_SCTP_COOKIE_INC:
					ops->cinc = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_COOKIE_INC;
					continue;
				case T_SCTP_THROTTLE_ITVL:
					ops->titvl = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_THROTTLE_ITVL;
					continue;
				case T_SCTP_MAC_TYPE:
					ops->mac = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_MAC_TYPE;
					continue;
				case T_SCTP_HB:
					ops->hb = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_HB;
					continue;
				case T_SCTP_RTO:
					ops->rto = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_RTO;
					continue;
				case T_SCTP_MAXSEG:
					ops->mss = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_MAXSEG;
					continue;
				case T_SCTP_STATUS:
					ops->stat = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_STATUS;
					continue;
				case T_SCTP_DEBUG:
					ops->debug = (void *) _T_OPT_DATA_OFS(oh, 0);
					ops->flags |= TF_SCTP_DEBUG;
					continue;
				}
			break;
#endif
		}
	}
	if (oh)
		return (TBADOPT);
	return (0);
}

STATIC size_t ss_cmsg_size(ss_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
#ifdef UDP_CHECKSUM
		if (ops->csum && flags & TF_UDP_CHECKSUM)
			len += hlen + CMSG_ALIGN(sizeof(*(ops->csum)));
#endif
#if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE)
#endif
	}
	return (len);
}
STATIC void ss_build_cmsg(ss_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) {
		if (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->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->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->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->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->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->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->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->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->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 int ss_parse_cmsg(struct msghdr *msg, ss_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;
#if defined(CONFIG_SCTP)||defined(CONFIG_SCTP_MODULE)
		case SOL_SCTP:
			switch (cmsg->cmsg_type) {
			case SCTP_NODELAY:
			case SCTP_MAXSEG:
			case SCTP_CORK:
			case SCTP_RECVSID:
			case SCTP_RECVPPI:
			case SCTP_RECVTSN:
			case SCTP_SID:
			case SCTP_PPI:
			case SCTP_SSN:
			case SCTP_TSN:
			case SCTP_HB:
			case SCTP_RTO:
			case SCTP_COOKIE_LIFE:
			case SCTP_SACK_DELAY:
			case SCTP_PATH_MAX_RETRANS:
			case SCTP_ASSOC_MAX_RETRANS:
			case SCTP_MAX_INIT_RETRIES:
			case SCTP_HEARTBEAT_ITVL:
			case SCTP_RTO_INITIAL:
			case SCTP_RTO_MIN:
			case SCTP_RTO_MAX:
			case SCTP_OSTREAMS:
			case SCTP_ISTREAMS:
			case SCTP_COOKIE_INC:
			case SCTP_THROTTLE_ITVL:
			case SCTP_MAC_TYPE:
			case SCTP_CKSUM_TYPE:
			case SCTP_DEBUG_OPTIONS:
			case SCTP_STATUS:
			case SCTP_ALI:
			case SCTP_PR:
			case SCTP_DISPOSITION:
			case SCTP_LIFETIME:
			case SCTP_ADD:
			case SCTP_ADD_IP:
			case SCTP_DEL_IP:
			case SCTP_SET:
			case SCTP_SET_IP:
			case SCTP_ECN:
			case SCTP_MAX_BURST:
				break;
			}
			break;
#endif
		default:
			break;
		}
	}
	return (0);
}

/*
 *  =========================================================================
 *
 *  OPTIONS Handling
 *
 *  =========================================================================
 */
STATIC ss_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 int ss_opt_check(ss_t * ss, ss_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 (ss->p.prot.protocol == 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 (ss->p.prot.protocol == T_INET_UDP) {
			if (opt->csum)
				opt->flags |= TF_UDP_CHECKSUM;
		}
	}
	return (0);
}

/*
 *  DEFAULT Options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC int ss_opt_default(ss_t * ss, ss_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 (ss->p.prot.protocol == 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 (ss->p.prot.protocol == T_INET_UDP) {
		if (!flags || opt->csum) {
			opt->csum = &opt_defaults.csum;
			opt->flags |= TF_UDP_CHECKSUM;
		}
	}
	return (0);
}

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

/*
 *  CURRENT Options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC int ss_opt_current(ss_t * ss, ss_opts_t * opt)
{
	uint flags = opt->flags;
	opt->flags = 0;
	if (!flags || opt->opt) {
		opt->opt = &ss->options.opt;
		opt->flags |= TF_IP_OPTIONS;
	}
	if (!flags || opt->tos) {
		opt->tos = &ss->options.tos;
		opt->flags |= TF_IP_TOS;
	}
	if (!flags || opt->ttl) {
		opt->ttl = &ss->options.ttl;
		opt->flags |= TF_IP_TTL;
	}
	if (!flags || opt->reuse) {
		opt->reuse = &ss->options.reuse;
		opt->flags |= TF_IP_REUSEADDR;
	}
	if (!flags || opt->norte) {
		opt->norte = &ss->options.norte;
		opt->flags |= TF_IP_DONTROUTE;
	}
	if (!flags || opt->bcast) {
		opt->bcast = &ss->options.bcast;
		opt->flags |= TF_IP_BROADCAST;
	}
	if (ss->p.prot.protocol == T_INET_TCP) {
		if (!flags || opt->nodly) {
			opt->nodly = &ss->options.nodly;
			opt->flags |= TF_TCP_NODELAY;
		}
		if (!flags || opt->mss) {
			opt->mss = &ss->options.mss;
			opt->flags |= TF_TCP_MAXSEG;
		}
		if (!flags || opt->alive) {
			opt->alive = &ss->options.alive;
			opt->flags |= TF_TCP_KEEPALIVE;
		}
	}
	if (ss->p.prot.protocol == T_INET_UDP) {
		if (!flags || opt->csum) {
			opt->csum = &ss->options.csum;
			opt->flags |= TF_UDP_CHECKSUM;
		}
	}
	return (0);
}

/*
 *  =========================================================================
 *
 *  STATE Changes
 *
 *  =========================================================================
 */
#ifdef _DEBUG
STATIC const char *state_name(long state)
{
	switch (state) {
	case TS_UNBND:
		return ("TS_UNBND");
	case TS_WACK_BREQ:
		return ("TS_WACK_BREQ");
	case TS_WACK_UREQ:
		return ("TS_WACK_UREQ");
	case TS_IDLE:
		return ("TS_IDLE");
	case TS_WACK_OPTREQ:
		return ("TS_WACK_OPTREQ");
	case TS_WACK_CREQ:
		return ("TS_WACK_CREQ");
	case TS_WCON_CREQ:
		return ("TS_WCON_CREQ");
	case TS_WRES_CIND:
		return ("TS_WRES_CIND");
	case TS_WACK_CRES:
		return ("TS_WACK_CRES");
	case TS_DATA_XFER:
		return ("TS_DATA_XFER");
	case TS_WIND_ORDREL:
		return ("TS_WIND_ORDREL");
	case TS_WREQ_ORDREL:
		return ("TS_WREQ_ORDREL");
	case TS_WACK_DREQ6:
		return ("TS_WACK_DREQ6");
	case TS_WACK_DREQ7:
		return ("TS_WACK_DREQ7");
	case TS_WACK_DREQ9:
		return ("TS_WACK_DREQ9");
	case TS_WACK_DREQ10:
		return ("TS_WACK_DREQ10");
	case TS_WACK_DREQ11:
		return ("TS_WACK_DREQ11");
	case TS_NOSTATES:
		return ("TS_NOSTATES");
	default:
		return ("(unknown)");
	}
}
#endif
STATIC void ss_set_state(ss_t * ss, long state)
{
	printd(("%s: %p: %s <- %s\n", SS_MOD_NAME, ss, state_name(state), state_name(ss->p.info.CURRENT_state)));
	ss->p.info.CURRENT_state = state;
}
STATIC long ss_get_state(ss_t * ss)
{
	return (ss->p.info.CURRENT_state);
}

#ifdef _DEBUG
STATIC const char *tcp_state_name(int state)
{
	switch (state) {
	case TCP_ESTABLISHED:
		return ("TCP_ESTABLISHED");
	case TCP_SYN_SENT:
		return ("TCP_SYN_SENT");
	case TCP_SYN_RECV:
		return ("TCP_SYN_RECV");
	case TCP_FIN_WAIT1:
		return ("TCP_FIN_WAIT1");
	case TCP_FIN_WAIT2:
		return ("TCP_FIN_WAIT2");
	case TCP_TIME_WAIT:
		return ("TCP_TIME_WAIT");
	case TCP_CLOSE:
		return ("TCP_CLOSE");
	case TCP_CLOSE_WAIT:
		return ("TCP_CLOSE_WAIT");
	case TCP_LAST_ACK:
		return ("TCP_LAST_ACK");
	case TCP_LISTEN:
		return ("TCP_LISTEN");
	case TCP_CLOSING:
		return ("TCP_CLOSING");
	case TCP_MAX_STATES:
		return ("TCP_MAX_STATES");
	default:
		return ("(unknown)");
	}
}
#endif

/*
 *  ------------------------------------------------------------------------
 *
 *  Socket Calls
 *
 *  ------------------------------------------------------------------------
 *  These are wrappered versions of socket calls.
 */
/*
 *  SOCKET CREATE
 *  ------------------------------------------------------------------------
 */
STATIC int ss_socket(ss_t * ss)
{
	int err;
	int family, type, protocol;
	ensure(ss, return (-EFAULT));
	unless(ss->sock, return (-EFAULT));
	family = ss->p.prot.family;
	type = ss->p.prot.type;
	protocol = (ss->p.prot.protocol == IPPROTO_RAW) ? ss->port : ss->p.prot.protocol;
	printd(("%s: %p: SS_CREATE %d:%d:%d\n", SS_MOD_NAME, ss, family, type, protocol));
	if (!(err = sock_create(family, type, protocol, &ss->sock))) {
		ensure(ss->sock, return (-EFAULT));
		ensure(ss->sock->sk, return (-EFAULT));
		ss->sock->sk->allocation = GFP_ATOMIC;
		ss_socket_get(ss->sock, ss);
		return (0);
	}
	printd(("%s: %p: ERROR: from sock_create %d\n", SS_MOD_NAME, ss, err));
	return (err);
}

/*
 *  SOCKET BIND
 *  ------------------------------------------------------------------------
 */
STATIC int ss_bind(ss_t * ss, ss_addr_t * add)
{
	int err;
	ensure(ss, return (-EFAULT));
	ensure(ss->sock, return (-EFAULT));
	ensure(ss->sock->sk, return (-EFAULT));
	ensure(ss->sock->ops, return (-EFAULT));
	ensure(ss->sock->ops->bind, return (-EFAULT));
	printd(("%s: %p: SS_BIND\n", SS_MOD_NAME, ss));
	if (!(err = ss->sock->ops->bind(ss->sock, (struct sockaddr *) add, sizeof(*add)))) {
		ss->src = *add;
		return (0);
	}
	printd(("%s: %p: ERROR: from sock->ops->bind %d\n", SS_MOD_NAME, ss, err));
	return (err);
}

/*
 *  SOCKET LISTEN
 *  ------------------------------------------------------------------------
 */
STATIC int ss_listen(ss_t * ss, uint cons)
{
	int err;
	int type;
	ensure(ss, return (-EFAULT));
	ensure(ss->sock, return (-EFAULT));
	ensure(ss->sock->sk, return (-EFAULT));
	ensure(ss->sock->ops, return (-EFAULT));
	ensure(ss->sock->ops->listen, return (-EFAULT));
	type = ss->p.prot.type;
	ensure(type == SOCK_STREAM || type == SOCK_SEQPACKET, return (-EFAULT));
	printd(("%s: %p: SS_LISTEN %d\n", SS_MOD_NAME, ss, cons));
	if (!(err = ss->sock->ops->listen(ss->sock, cons))) {
		ss->conind = cons;
		ss->tcp_state = ss->sock->sk->state;
		return (0);
	}
	printd(("%s: %p: ERROR: from sock->ops->listen %d\n", SS_MOD_NAME, ss, err));
	return (err);
}

/*
 *  SOCKET ACCEPT
 *  ------------------------------------------------------------------------
 *  Unfortunately, sock->ops->accept will only accept the next sequential
 *  connection indication.  In TLI's case, we want to be able to accept or
 *  release connection indications other than the next sequential indication.
 *  To do this we must muck with TCP's accept queue when the SEQ_number is not
 *  the next in the queue.  To do this we mimic some of the tcp_accept
 *  behavior.
 */
STATIC int ss_accept(ss_t * ss, struct socket **newsock, mblk_t * cp)
{
	struct socket *sock;
	ensure(newsock, return (-EFAULT));
	ensure(cp, return (-EFAULT));
	ensure(ss, return (-EFAULT));
	ensure(ss->sock, return (-EFAULT));
	ensure(ss->sock->sk, return (-EFAULT));
	ensure(ss->sock->ops, return (-EFAULT));
	ensure(ss->sock->ops->accept, return (-EFAULT));
	printd(("%s: %p: SS_ACCEPT\n", SS_MOD_NAME, ss));
	if ((sock = sock_alloc())) {
		struct sock *sk = ss->sock->sk;
		struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
		struct open_request *req, *req_prev, **reqp;
		struct sock *ask = ((ss_event_t *) cp->b_rptr)->sk;
		sock->type = ss->sock->type;
		sock->ops = ss->sock->ops;
		lock_sock(sk);
		if (tp->accept_queue) {
			/* find connection in queue */
			for (reqp = &tp->accept_queue, req_prev = NULL; *reqp && (*reqp)->sk != ask;
			     req_prev = (*reqp), reqp = &(*reqp)->dl_next) ;
			if ((req = *reqp)) {
				if (!((*reqp) = (*reqp)->dl_next))
					tp->accept_queue_tail = req_prev;
				sk->ack_backlog--;
				tcp_openreq_fastfree(req);
			}
			release_sock(sk);
			lock_sock(ask);
			sock_graft(ask, sock);
			release_sock(ask);
			sock->state = SS_CONNECTED;
			*newsock = sock;
			bufq_unlink(&ss->conq, cp);
			freemsg(cp);
			return (0);
		}
		release_sock(sk);
		ss_socket_put(sock);
		printd(("%s: %p: invalid accept\n", SS_MOD_NAME, ss));
		return (-EAGAIN);
	}
	printd(("%s: %p: ERROR: couldn't allocate accepting socket\n", SS_MOD_NAME, ss));
	return (-EFAULT);
}

/*
 *  SOCKET UNBIND
 *  ------------------------------------------------------------------------
 *  There is no good way to unbind and rebind a socket in Linux, so we just
 *  close the entire socket.  The next time we go to bind, we will create
 *  a fresh socket to bind.
 */
STATIC int ss_unbind(ss_t * ss)
{
	ensure(ss, return (-EFAULT));
	ensure(ss->sock, return (-EFAULT));
	ensure(ss->sock->sk, return (-EFAULT));
	printd(("%s: %p: SS_UNBIND\n", SS_MOD_NAME, ss));
	ss_socket_put(xchg(&ss->sock, NULL));
	return (0);
}

/*
 *  SOCKET CONNECT
 *  ------------------------------------------------------------------------
 */
STATIC int ss_connect(ss_t * ss, ss_addr_t * dst)
{
	int err;
	ensure(ss, return (-EFAULT));
	ensure(ss->sock, return (-EFAULT));
	ensure(ss->sock->sk, return (-EFAULT));
	ensure(ss->sock->ops, return (-EFAULT));
	ensure(ss->sock->ops->connect, return (-EFAULT));
	if ((err = ss->sock->ops->connect(ss->sock, (struct sockaddr *) dst, sizeof(*dst), O_NONBLOCK)) == 0 ||
	    err == -EINPROGRESS) {
		return (0);
	}
	printd(("%s: %p: ERROR: from sock->ops->connect %d\n", SS_MOD_NAME, ss, err));
	return (err);
}

/*
 *  SOCKET SENDMSG
 *  ------------------------------------------------------------------------
 */
STATIC int ss_sendmsg(ss_t * ss, struct msghdr *msg, int len)
{
	int res;
	ensure(ss, return (-EFAULT));
	ensure(ss->sock, return (-EFAULT));
	ensure(ss->sock->ops, return (-EFAULT));
	ensure(ss->sock->ops->sendmsg, return (-EFAULT));
	ensure(ss->sock->sk, return (-EFAULT));
	ensure(ss->sock->sk->prot, return (-EFAULT));
	ensure(ss->sock->sk->prot->sendmsg, return (-EFAULT));
	{
		mm_segment_t fs = get_fs();
		set_fs(KERNEL_DS);
		res = sock_sendmsg(ss->sock, msg, len);
		set_fs(fs);
	}
	if (res <= 0)
		printd(("%s: %p: ERROR: from sock->sk->prot->sendmsg %d\n", SS_MOD_NAME, ss, res));
	return (res);
}

/*
 *  SOCKET RECVMSG
 *  ------------------------------------------------------------------------
 */
STATIC int ss_recvmsg(ss_t * ss, struct msghdr *msg, int size)
{
	int res;
	int sflags = MSG_DONTWAIT | MSG_NOSIGNAL;
	ensure(ss, return (-EFAULT));
	ensure(ss->sock, return (-EFAULT));
	ensure(ss->sock->ops, return (-EFAULT));
	ensure(ss->sock->ops->recvmsg, return (-EFAULT));
	ensure(ss->sock->sk, return (-EFAULT));
	ensure(ss->sock->sk->prot, return (-EFAULT));
	ensure(ss->sock->sk->prot->recvmsg, return (-EFAULT));
	{
		mm_segment_t fs = get_fs();
		set_fs(KERNEL_DS);
		res = sock_recvmsg(ss->sock, msg, size, sflags);
		set_fs(fs);
	}
	if (res < 0)
		printd(("%s: %p: ERROR: from sock->ops->recvmsg %d\n", SS_MOD_NAME, ss, res));
	return (res);
}

/*
 *  SOCKET DISCONNECT
 *  ------------------------------------------------------------------------
 *  Performing a sock_release (ss_socket_put) from the established state does
 *  not affect an abortive release for TCP, but rather, initiates an orderly
 *  shutdown rather than an abortive release.  We can try performing a
 *  protocol disconnect and see if that works better.
 */
STATIC int ss_disconnect(ss_t * ss)
{
	int err;
	ensure(ss, return (-EFAULT));
	ensure(ss->sock->ops, return (-EFAULT));
	ensure(ss->sock->ops->connect, return (-EFAULT));
	ensure(ss->sock->sk, return (-EFAULT));
	ensure(ss->sock->sk->prot, return (-EFAULT));
	ensure(ss->sock->sk->prot->disconnect, return (-EFAULT));
	if (!(err = ss->sock->sk->prot->disconnect(ss->sock->sk, O_NONBLOCK))) {
		ss->sock->state = SS_UNCONNECTED;
		return (0);
	}
	ss->sock->state = SS_DISCONNECTING;
	printd(("%s: %p: ERROR: from sock->sk->prot->disconnect %d\n", SS_MOD_NAME, ss, err));
	return (err);
}

/*
 *  =========================================================================
 *
 *  IP T-Provider --> T-User Primitives (Indication, Confirmation and Ack)
 *
 *  =========================================================================
 */
/*
 *  M_ERROR
 *  ---------------------------------------------------------------
 */
STATIC int m_error(queue_t * q, int error)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	int hangup = 0;
	if (error < 0)
		error = -error;
	switch (error) {
	case EBUSY:
	case ENOBUFS:
	case EAGAIN:
	case ENOMEM:
		return (-error);
	case EPIPE:
	case ENETDOWN:
	case EHOSTUNREACH:
		hangup = 1;
	}
	if ((mp = ss_allocb(q, 2, BPRI_HI))) {
		if (ss->sock)
			ss_socket_put(xchg(&ss->sock, NULL));
		if (hangup) {
			printd(("%s: %p: <- M_HANGUP\n", SS_MOD_NAME, ss));
			mp->b_datap->db_type = M_HANGUP;
			putnext(ss->rq, mp);
			error = EPIPE;
		} else {
			printd(("%s: %p: <- M_ERROR %d\n", SS_MOD_NAME, ss, error));
			mp->b_datap->db_type = M_ERROR;
			*(mp->b_wptr)++ = error;
			*(mp->b_wptr)++ = error;
			putnext(ss->rq, mp);
		}
		return (-error);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  T_CONN_IND          11 - Connection Indication
 *  ---------------------------------------------------------------
 */
STATIC int t_conn_ind(queue_t * q, ss_addr_t * src, ss_opts_t * opts, mblk_t * cp)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_conn_ind *p;
	size_t src_len = src ? sizeof(*src) : 0;
	size_t opt_len = ss_opts_size(ss, opts);
	if (bufq_length(&ss->conq) <= ss->conind) {
		if (canputnext(ss->rq)) {
			if ((mp = ss_allocb(q, sizeof(*p) + src_len + opt_len, BPRI_MED))) {
				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) {
					ss_build_opts(ss, opts, &mp->b_wptr);
				}
				bufq_queue(&ss->conq, cp);
				ss_set_state(ss, TS_WRES_CIND);
				printd(("%s: %p: <- T_CONN_IND\n", SS_MOD_NAME, ss));
				putnext(ss->rq, mp);
				return (QR_ABSORBED);	/* absorbed cp */
			}
			ptrace(("%s: ERROR: no buffers\n", SS_MOD_NAME));
			return (-ENOBUFS);
		}
		ptrace(("%s: ERROR: flow controlled\n", SS_MOD_NAME));
		return (-EBUSY);
	}
	ptrace(("%s: ERROR: too many conn inds\n", SS_MOD_NAME));
	return (-EAGAIN);
}

/*
 *  T_CONN_CON          12 - Connection Confirmation
 *  ---------------------------------------------------------------
 */
STATIC int t_conn_con(queue_t * q, ss_addr_t * res, ss_opts_t * opts, mblk_t * dp)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_conn_con *p;
	size_t res_len = res ? sizeof(*res) : 0;
	size_t opt_len = ss_opts_size(ss, opts);
	/* 
	 * this shouldn't happen, we probably shouldn't even check 
	 */
	if (canputnext(ss->rq)) {
		if ((mp = ss_allocb(q, sizeof(*p) + res_len + opt_len, BPRI_MED))) {
			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) {
				ss_build_opts(ss, opts, &mp->b_wptr);
			}
			ss_set_state(ss, TS_DATA_XFER);
			printd(("%s: %p: <- T_CONN_CON\n", SS_MOD_NAME, ss));
			putnext(ss->rq, mp);
			return (0);
		}
		ptrace(("%s: ERROR: no buffers\n", SS_MOD_NAME));
		return (-ENOBUFS);
	}
	ptrace(("%s: ERROR: flow controlled\n", SS_MOD_NAME));
	return (-EBUSY);
}

/*
 *  T_DISCON_IND        13 - Disconnect Indication
 *  ---------------------------------------------------------------
 */
STATIC mblk_t *t_seq_find(ss_t * ss, mblk_t * rp)
{
	mblk_t *mp;
	if ((mp = rp)) {
		struct sock *sk = ((ss_event_t *) rp->b_rptr)->sk;
		lis_spin_lock(&ss->conq.q_lock);
		{
			for (mp = bufq_head(&ss->conq); mp && ((ss_event_t *) mp->b_rptr)->sk != sk;
			     mp = mp->b_next) ;
		}
		lis_spin_unlock(&ss->conq.q_lock);
	}
	return (mp);
}
STATIC ulong t_seq_delete(ss_t * ss, mblk_t * rp)
{
	mblk_t *mp;
	if ((mp = t_seq_find(ss, rp))) {
		struct socket *sock = NULL;
		if (!ss_accept(ss, &sock, rp) && sock)
			sock_release(sock);
		return ((ulong) mp);
	}
	return (0);
}
STATIC int t_discon_ind(queue_t * q, ss_addr_t * res, uint orig, uint reason, mblk_t * cp, mblk_t * dp)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_discon_ind *p;
	ulong seq = 0;
	(void) res;
	if (canputnext(ss->rq)) {
		if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) {
			if (!cp || (seq = t_seq_delete(ss, cp))) {
				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(&ss->conq))
					ss_set_state(ss, TS_IDLE);
				else
					ss_set_state(ss, TS_WRES_CIND);
				mp->b_cont = dp;
				printd(("%s: %p: <- T_DISCON_IND\n", SS_MOD_NAME, ss));
				putnext(ss->rq, mp);
				return (0);
			}
			freemsg(mp);
			ptrace(("%s: ERROR: bad sequence number\n", SS_MOD_NAME));
			return (-EFAULT);
		}
		ptrace(("%s: ERROR: no buffers\n", SS_MOD_NAME));
		return (-ENOBUFS);
	}
	ptrace(("%s: ERROR: flow controlled\n", SS_MOD_NAME));
	return (-EBUSY);
}

/*
 *  T_DATA_IND          14 - Data Indication
 *  ---------------------------------------------------------------
 */
STATIC int t_data_ind(queue_t * q, uint flags, mblk_t * dp)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_data_ind *p;
	if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) {
		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;
		printd(("%s: %p: <- T_DATA_IND\n", SS_MOD_NAME, ss));
		putnext(ss->rq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: ERROR: no buffers\n", SS_MOD_NAME));
	return (-ENOBUFS);
}

/*
 *  T_EXDATA_IND        15 - Expedited Data Indication
 *  ---------------------------------------------------------------
 */
STATIC int t_exdata_ind(queue_t * q, uint flags, mblk_t * dp)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_exdata_ind *p;
	if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) {
		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;
		printd(("%s: %p: <- T_EXDATA_IND\n", SS_MOD_NAME, ss));
		putnext(ss->rq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: ERROR: no buffers\n", SS_MOD_NAME));
	return (-ENOBUFS);
}

/*
 *  T_INFO_ACK          16 - Information acknowledgement
 *  ---------------------------------------------------------------
 */
STATIC int t_info_ack(queue_t * q)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_info_ack *p;
	if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		*p = ss->p.info;
		printd(("%s: %p: <- T_INFO_ACK\n", SS_MOD_NAME, ss));
		putnext(ss->rq, mp);
		return (0);
	}
	ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME));
	return (-ENOBUFS);
}

/*
 *  T_BIND_ACK          17 - Bind Acknowledgement
 *  ---------------------------------------------------------------
 */
STATIC int t_bind_ack(queue_t * q, ss_addr_t * add, ulong conind)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_bind_ack *p;
	size_t add_len = add ? sizeof(*add) : 0;
	if ((mp = ss_allocb(q, sizeof(*p) + add_len, BPRI_MED))) {
		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 = conind;
		if (add_len) {
			bcopy(add, mp->b_wptr, add_len);
			mp->b_wptr += add_len;
		}
		ss_set_state(ss, TS_IDLE);
		printd(("%s: %p: <- T_BIND_ACK\n", SS_MOD_NAME, ss));
		putnext(ss->rq, mp);
		return (0);
	}
	ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME));
	return (-ENOBUFS);
}

/*
 *  T_ERROR_ACK         18 - Error Acknowledgement
 *  -------------------------------------------------------------------------
 */
STATIC int t_error_ack(queue_t * q, ulong prim, long error)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_error_ack *p;
	switch (error) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (error);
	case 0:
		never();
		return (error);
	}
	if (!(mp = ss_allocb(q, sizeof(*p), 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 (ss_get_state(ss)) {
#ifdef TS_WACK_OPTREQ
		case TS_WACK_OPTREQ:
			ss_set_state(ss, TS_IDLE);
			break;
#endif
		case TS_WACK_UREQ:
			ss_set_state(ss, TS_IDLE);
			break;
		case TS_WACK_CREQ:
			ss_set_state(ss, TS_IDLE);
			break;
		case TS_WACK_BREQ:
			ss_set_state(ss, TS_UNBND);
			break;
		case TS_WACK_CRES:
			ss_set_state(ss, TS_WRES_CIND);
			break;
		case TS_WACK_DREQ6:
			ss_set_state(ss, TS_WCON_CREQ);
			break;
		case TS_WACK_DREQ7:
			ss_set_state(ss, TS_WRES_CIND);
			break;
		case TS_WACK_DREQ9:
			ss_set_state(ss, TS_DATA_XFER);
			break;
		case TS_WACK_DREQ10:
			ss_set_state(ss, TS_WIND_ORDREL);
			break;
		case TS_WACK_DREQ11:
			ss_set_state(ss, 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.
			 */
		}
	}
	printd(("%s: %p: <- T_ERROR_ACK\n", SS_MOD_NAME, ss));
	putnext(ss->rq, mp);
	/* 
	 *  Returning -EPROTO here will make sure that the old state is
	 *  restored correctly.  If we return QR_DONE, then the state will
	 *  never be restored.
	 */
	if (error < 0)
		return (error);
	return (-EPROTO);
      enobufs:
	ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME));
	return (-ENOBUFS);
}

/*
 *  T_OK_ACK            19 - Success Acknowledgement
 *  -------------------------------------------------------------------------
 */
STATIC int t_ok_ack(queue_t * q, ulong prim, mblk_t * cp, ss_t * as)
{
	int err = -EFAULT;
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_ok_ack *p;
	struct socket *sock = NULL;
	if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_OK_ACK;
		p->CORRECT_prim = prim;
		switch (ss_get_state(ss)) {
		case TS_WACK_CREQ:
			if ((err = ss_connect(ss, &ss->dst)))
				goto free_error;
			ss_set_state(ss, TS_WCON_CREQ);
			break;
		case TS_WACK_UREQ:
			if ((err = ss_unbind(ss)))
				goto free_error;
			ss_set_state(ss, TS_UNBND);
			break;
		case TS_WACK_CRES:
			ensure(as && cp, goto free_error);
			if ((err = ss_accept(ss, &sock, cp)))
				goto free_error;
			if (as->sock)
				ss_socket_put(as->sock);	/* get rid of old socket */
			as->sock = sock;
			ss_socket_get(as->sock, as);
			ss_set_state(as, TS_DATA_XFER);
			if (bufq_length(&ss->conq))
				ss_set_state(ss, TS_WRES_CIND);
			else {
				ss_set_state(ss, TS_IDLE);
				/* make sure any backed up indications are processed */
				qenable(ss->rq);
			}
			break;
		case TS_WACK_DREQ7:
			ensure(cp, goto free_error);
			if (!(err = ss_accept(ss, &sock, cp)))
				sock_release(sock);
			if (bufq_length(&ss->conq))
				ss_set_state(ss, TS_WRES_CIND);
			else {
				ss_set_state(ss, TS_IDLE);
				/* make sure any backed up indications are processed */
				qenable(ss->rq);
			}
			break;
		case TS_WACK_DREQ6:
		case TS_WACK_DREQ9:
		case TS_WACK_DREQ10:
		case TS_WACK_DREQ11:
			if ((err = ss_disconnect(ss)))
				goto free_error;
			ss_set_state(ss, 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.
			 */
		}
		printd(("%s: %p: <- T_OK_ACK\n", SS_MOD_NAME, ss));
		putnext(ss->rq, mp);
		return (QR_DONE);
	}
	ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME));
	return (-ENOBUFS);
      free_error:
	freemsg(mp);
	return t_error_ack(q, prim, err);
}

/*
 *  T_UNIDATA_IND       20 - Unitdata indication
 *  -------------------------------------------------------------------------
 */
STATIC int t_unitdata_ind(queue_t * q, ss_addr_t * src, ss_opts_t * opts, mblk_t * dp)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_unitdata_ind *p;
	size_t src_len = src ? sizeof(*src) : 0;
	size_t opt_len = ss_opts_size(ss, opts);
	if ((mp = ss_allocb(q, sizeof(*p) + src_len + opt_len, BPRI_MED))) {
		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) {
			ss_build_opts(ss, opts, &mp->b_wptr);
		}
		mp->b_cont = dp;
		printd(("%s: %p: <- T_UNITDATA_IND\n", SS_MOD_NAME, ss));
		putnext(ss->rq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME));
	return (-ENOBUFS);
}

/*
 *  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, ss_addr_t * src, ss_addr_t * dst, ss_opts_t * opts, mblk_t * dp,
				int etype)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_uderror_ind *p;
	size_t dst_len = dst ? sizeof(*dst) : 0;
	size_t opt_len = ss_opts_size(ss, opts);
	(void) src;
	if (canputnext(ss->rq)) {
		if ((mp = ss_allocb(q, sizeof(*p) + dst_len + opt_len, BPRI_MED))) {
			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) {
				ss_build_opts(ss, opts, &mp->b_wptr);
			}
			printd(("%s: %p: <- T_UDERROR_IND\n", SS_MOD_NAME, ss));
			putnext(ss->rq, mp);
			return (0);
		}
		ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME));
		return (-ENOBUFS);
	}
	ptrace(("%s: ERROR: Flow controlled\n", SS_MOD_NAME));
	return (-EBUSY);
}

/*
 *  T_OPTMGMT_ACK       22 - Options Management Acknowledge
 *  -------------------------------------------------------------------------
 */
STATIC int t_optmgmt_ack(queue_t * q, ulong flags, ss_opts_t * opts)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_optmgmt_ack *p;
	size_t opt_len = ss_opts_size(ss, opts);
	if ((mp = ss_allocb(q, sizeof(*p) + opt_len, BPRI_MED))) {
		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) {
			ss_build_opts(ss, opts, &mp->b_wptr);
		}
#ifdef TS_WACK_OPTREQ
		if (ss_get_state(ss) == TS_WACK_OPTREQ)
			ss_set_state(ss, TS_IDLE);
#endif
		printd(("%s: %p: <- T_OPTMGMT_ACK\n", SS_MOD_NAME, ss));
		putnext(ss->rq, mp);
		return (0);
	}
	ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME));
	return (-ENOBUFS);
}

/*
 *  T_ORDREL_IND        23 - Orderly Release Indication
 *  -------------------------------------------------------------------------
 */
STATIC int t_ordrel_ind(queue_t * q)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_ordrel_ind *p;
	if (canputnext(ss->rq)) {
		if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((typeof(p)) mp->b_wptr)++;
			p->PRIM_type = T_ORDREL_IND;
			switch (ss_get_state(ss)) {
			case TS_DATA_XFER:
				ss_set_state(ss, TS_WREQ_ORDREL);
				break;
			case TS_WIND_ORDREL:
				ss_set_state(ss, TS_IDLE);
				break;
			}
			printd(("%s: %p: <- T_ORDREL_IND\n", SS_MOD_NAME, ss));
			putnext(ss->rq, mp);
			return (0);
		}
		ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME));
		return (-ENOBUFS);
	}
	ptrace(("%s: ERROR: Flow controlled\n", SS_MOD_NAME));
	return (-EBUSY);
}

/*
 *  T_OPTDATA_IND       26 - Data with Options Indication
 *  -------------------------------------------------------------------------
 */
STATIC int t_optdata_ind(queue_t * q, uint flags, ss_opts_t * opts, mblk_t * dp)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_optdata_ind *p;
	size_t opt_len = ss_opts_size(ss, opts);
	if ((mp = ss_allocb(q, sizeof(*p) + opt_len, BPRI_MED))) {
		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) {
			ss_build_opts(ss, opts, &mp->b_wptr);
		}
		mp->b_cont = dp;
		printd(("%s: %p: <- T_OPTDATA_IND\n", SS_MOD_NAME, ss));
		putnext(ss->rq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME));
	return (-ENOBUFS);
}

#ifdef T_ADDR_ACK
/*
 *  T_ADDR_ACK          27 - Address Acknowledgement
 *  -------------------------------------------------------------------------
 */
STATIC int t_addr_ack(queue_t * q, ss_addr_t * loc, ss_addr_t * rem)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_addr_ack *p;
	size_t loc_len = loc ? sizeof(*loc) : 0;
	size_t rem_len = rem ? sizeof(*rem) : 0;
	if ((mp = ss_allocb(q, sizeof(*p) + loc_len + rem_len, BPRI_MED))) {
		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;
		}
		printd(("%s: %p: <- T_ADDR_ACK\n", SS_MOD_NAME, ss));
		putnext(ss->rq, mp);
		return (0);
	}
	ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME));
	return (-ENOBUFS);
}
#endif
#ifdef T_CAPABILITY_ACK
/*
 *  T_CAPABILITY_ACK    ?? - Protocol Capability Ack
 *  -------------------------------------------------------------------------
 */
STATIC int t_capability_ack(queue_t * q, ulong caps)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	struct T_capability_ack *p;
	if ((mp = ss_allocb(q, sizeof(*p), BPRI_MED))) {
		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) ss->rq : 0;
		if (caps & TC1_INFO)
			p->INFO_ack = ss->p.info;
		else
			bzero(&p->INFO_ack, sizeof(p->INFO_ack));
		printd(("%s: %p: <- T_CAPABILITY_ACK\n", SS_MOD_NAME, ss));
		putnext(ss->rq, mp);
		return (0);
	}
	ptrace(("%s: ERROR: No buffers\n", SS_MOD_NAME));
	return (-ENOBUFS);
}
#endif

/*
 *  =========================================================================
 *
 *  CONNECTION INNDICATIONS
 *
 *  =========================================================================
 */
/*
 *  CONIND
 *  -------------------------------------------------------------------------
 *  An unfortunate mis-match between the socket model and the TLI model is
 *  that in TLI it is possible to determine the remote address from a
 *  T_CONN_IND *before* accepting the connection.  Under sockets, the
 *  connection must be accepted before it is possible to determine the peer's
 *  address.  This means that connection must be accepted and then released,
 *  rather than refused.
 */
STATIC int ss_conn_ind(queue_t * q, mblk_t * cp)
{
	ss_t *ss = PRIV(q);
	struct sock *sk;
	struct ss_event *p = (typeof(p)) cp->b_rptr;
	ss_addr_t dst = { AF_UNSPEC, 0, };
	ss_opts_t opt = { 0, };
	if ((1 << ss_get_state(ss)) & ~TSM_LISTEN)
		goto outstate;
	if (!ss->conind || ss->sock->sk->state != TCP_LISTEN)
		goto nolisten;
	if (cp->b_wptr < cp->b_rptr + sizeof(*p))
		goto einval;
	if (ss->sock->sk == p->sk)
		goto duplicate;
	sk = p->sk;
	dst.sin_family = AF_INET;
	dst.sin_port = sk->dport;
	dst.sin_addr.s_addr = sk->daddr;
#ifdef LINUX_2_4
	opt.opt = sk->protinfo.af_inet.opt;
	opt.tos = (ulong *) & sk->protinfo.af_inet.tos;
	opt.ttl = (ulong *) & sk->protinfo.af_inet.ttl;
#else
	opt.opt = sk->opt;
	opt.tos = (ulong *) & sk->ip_tos;
	opt.ttl = (ulong *) & sk->ip_ttl;
#endif
	opt.reuse = (ulong *) & sk->reuse;
	opt.norte = (ulong *) & sk->localroute;
	opt.bcast = (ulong *) & sk->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 (ss->p.prot.protocol == T_INET_TCP) {
#ifdef LINUX_2_4
		opt.nodly = (ulong *) & sk->tp_pinfo.af_tcp.nonagle;
#else
		opt.nodly = (ulong *) & sk->nonagle;
#endif
		opt.mss = (ulong *) & sk->tp_pinfo.af_tcp.user_mss;
		opt.alive = (ulong *) & sk->keepopen;
		opt.flags |= TF_TCP_NODELAY;
		opt.flags |= TF_TCP_MAXSEG;
		opt.flags |= TF_TCP_KEEPALIVE;
	}
	if (ss->p.prot.protocol == T_INET_SCTP) {
		/* 
		 * skip for now 
		 */
	}
	return t_conn_ind(q, &dst, &opt, cp);
      einval:
	swerr();
	ptrace(("%s: SWERR: invalid primitive format\n", SS_MOD_NAME));
	return (-EFAULT);
      outstate:
	ptrace(("%s: ERROR: connect indication in wrong state %ld\n", SS_MOD_NAME, ss_get_state(ss)));
	return (-EPROTO);
      nolisten:
	ptrace(("%s: ERROR: connect indication received while not listening\n", SS_MOD_NAME));
	return (-EPROTO);
      duplicate:
	ptrace(("%s: %p: INFO: discarding duplicate connection indication\n", SS_MOD_NAME, ss));
	return (QR_DONE);
}

/*
 *  =========================================================================
 *
 *  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 ss_sock_sendmsg(ss_t * ss, mblk_t * mp, ss_addr_t * dst, int flags, ss_opts_t * opts)
{
	int err = 0;
	int len, sdu, n;
	mblk_t *dp;
	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++;
		}
	}
	err = -EPROTO;
	if (!(ss->p.info.PROVIDER_flag & T_SNDZERO) && (!n || !len))
		goto out;
	if ((sdu = (int) ss->p.info.TSDU_size) > 0 && (!n || len > sdu))
		goto out;
	err = -EBUSY;
	if (len > sock_wspace(ss->sock->sk))
		goto out;
	/* 
	 *  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 = opts ? ss_cmsg_size(opts, TF_IP_OPTIONS | TF_IP_PKTINFO) : 0;
		unsigned char cbuf[clen > 1024 ? 1024 : clen];
		flags |= (MSG_DONTWAIT | MSG_NOSIGNAL);
		flags |= ss->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 = clen ? cbuf : NULL;
		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++;
			}
		}
		if (opts) {
			ss_build_cmsg(opts, &msg, TF_IP_OPTIONS | TF_IP_PKTINFO);
			ss_opt_negotiate(ss, opts);
		}
		ptrace(("%s: %p: sendmsg with len = %d\n", SS_MOD_NAME, ss, len));
		err = ss_sendmsg(ss, &msg, len);
	}
	if (err < 0)
		goto out;
	if (!err) {
		err = -EBUSY;
		goto out;
	}
	if (err >= len)
		return (QR_DONE);	/* full write */
	switch (ss->p.prot.type) {	/* partial write */
	case SOCK_DGRAM:
	case SOCK_RAW:
	case SOCK_RDM:
		err = -EMSGSIZE;
		goto out;
	case SOCK_SEQPACKET:
	case SOCK_STREAM:
		adjmsg(mp, err);
		err = -EAGAIN;
		goto out;
	}
	err = -EFAULT;
      out:
	switch (-err) {
	case 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.
		 */
		if (ss->wbid) {
			unbufcall(xchg(&ss->wbid, 0));
			ss->refcnt--;
		}
		ss->wbid = bufcall(len, BPRI_LO, &ss_bufsrv, (long) ss->wq);
		ss->refcnt++;
	default:
		return m_error(ss->rq, err);
	}
}

/*
 *  RECVMSG
 *  -------------------------------------------------------------------------
 */
STATIC int ss_sock_recvmsg(queue_t * q)
{
	ss_t *ss = PRIV(q);
	mblk_t *mp;
	int more, expd, res, oldsize = 0x7fffffff, size = 0;
	struct msghdr msg = { NULL, };
	unsigned char cbuf[128] = { 0, };
	ss_addr_t src = { AF_INET, 0, {0}, };
	struct sk_buff *skb;
	while (canputnext(ss->rq)) {
		switch (ss->p.prot.type) {
		case SOCK_DGRAM:
		case SOCK_RAW:
		case SOCK_RDM:
		case SOCK_STREAM:
		{
			size = (skb = skb_peek(&ss->sock->sk->receive_queue)) ? skb->len : 0;
			break;
		}
		case SOCK_SEQPACKET:
			size = atomic_read(&ss->sock->sk->rmem_alloc);
			break;
		}
		if (!size || size >= oldsize)
			return (QR_DONE);
		oldsize = size;
		if (!(mp = ss_allocb(ss->rq, size, BPRI_MED)))
			return (-ENOBUFS);
		{
			struct iovec iov[1];
			iov[0].iov_base = mp->b_rptr;
			iov[0].iov_len = size;
			if (ss->p.prot.type != SOCK_STREAM) {
				msg.msg_name = &src;
				msg.msg_namelen = sizeof(src);
			} else {
				msg.msg_name = NULL;
				msg.msg_namelen = 0;
			}
			msg.msg_iov = iov;
			msg.msg_iovlen = sizeof(iov) / sizeof(struct iovec);
			msg.msg_control = cbuf;
			msg.msg_controllen = sizeof(cbuf);
			msg.msg_flags = 0;
			if ((res = ss_recvmsg(ss, &msg, size)) < 0) {
				freemsg(mp);
				return (res);
			}
			if (res == 0) {
				freemsg(mp);
				return (QR_DONE);
			}
			mp->b_wptr = mp->b_rptr + res;
			ptrace(("%s: %p: recvmsg with len = %d\n", SS_MOD_NAME, ss, res));
		}
#if 0
		if (msg.msg_flags & MSG_TRUNC) {
			freemsg(mp);
			return (-EMSGSIZE);
		}
#endif
		{
			int err;
			ss_opts_t opt = { 0, };
			if (msg.msg_controllen)
				ss_parse_cmsg(&msg, &opt);
			more = (msg.msg_flags & MSG_EOR) ? 0 : T_ODF_MORE;
			expd = (msg.msg_flags & MSG_OOB) ? T_ODF_EX : 0;
			if (ss->p.info.SERV_type == T_CLTS) {
				if ((err = t_unitdata_ind(q, &src, &opt, mp)) != QR_ABSORBED)
					return (err);
			} else if (!opt.flags) {
				if (msg.msg_flags & MSG_OOB) {
					if ((err = t_exdata_ind(q, more, mp)) != QR_ABSORBED)
						return (err);
				} else {
					if ((err = t_data_ind(q, more, mp)) != QR_ABSORBED)
						return (err);
				}
			} else {
				if ((err = t_optdata_ind(q, more | expd, &opt, mp)) != QR_ABSORBED)
					return (err);
			}
		}
	}
	return (-EBUSY);
}

/*
 *  =========================================================================
 *
 *  STATE MACHINE
 *
 *  =========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  Socket CALLBACKS
 *
 *  -------------------------------------------------------------------------
 */
STATIC void ss_putctl(ss_t * ss, queue_t * q, int type, void (*func) (long), struct sock *sk)
{
	int flags;
	mblk_t *mp;
	ss_event_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_HI))) {
		mp->b_datap->db_type = type;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sk = sk;
		p->state = sk->state;	/* capture current state */
		putq(q, mp);
		return (void) (0);
	}
	/* 
	 * set up bufcall so we don't lose events 
	 */
	lis_spin_lock_irqsave(&ss->lock, &flags); {
		/* 
		 * make sure bufcalls don't happen now 
		 */
		if (q == ss->rq) {
			if (ss->rbid)
				unbufcall(xchg(&ss->rbid, 0));
			ss->rbid = bufcall(FASTBUF, BPRI_HI, func, (long) sk);
		} else if (q == ss->wq) {
			if (ss->wbid)
				unbufcall(xchg(&ss->wbid, 0));
			ss->wbid = bufcall(FASTBUF, BPRI_HI, func, (long) sk);
		}
	}
	lis_spin_unlock_irqrestore(&ss->lock, &flags);
}

/*
 *  STATE change
 *  -------------------------------------------------------------------------
 *  We get state changes on sockets that we hold.  We also get state changes
 *  on accepting sockets.
 */
STATIC void _ss_sock_state_change(long data)
{
	struct sock *sk;
	ss_t *ss;
	if ((sk = (struct sock *) data)) {
		if ((ss = SOCK_PRIV(sk))) {
			read_lock(&sk->callback_lock);
			ss_putctl(ss, ss->rq, M_PCRSE, &_ss_sock_state_change, sk);
			read_unlock(&sk->callback_lock);
		} else
			assure(ss);
	} else
		assure(sk);
}
STATIC void ss_state_change(struct sock *sk)
{
	if (sk) {
		ss_t *ss;
		if ((ss = SOCK_PRIV(sk))) {
			_ss_sock_state_change((long) sk);
			// ss->cb_save.state_change(sk);
		} else
			assure(ss);
	} else
		assure(sk);
}

/*
 *  WRITE Available
 *  -------------------------------------------------------------------------
 */
STATIC void _ss_sock_write_space(long data)
{
	struct sock *sk;
	ss_t *ss;
	if ((sk = (struct sock *) data)) {
		if ((ss = SOCK_PRIV(sk))) {
			read_lock(&sk->callback_lock);
			ss_putctl(ss, ss->wq, M_READ, &_ss_sock_write_space, sk);
			read_unlock(&sk->callback_lock);
		} else
			assure(ss);
	} else
		assure(sk);
}
STATIC void ss_write_space(struct sock *sk)
{
	if (sk) {
		ss_t *ss;
		if ((ss = SOCK_PRIV(sk))) {
			_ss_sock_write_space((long) sk);
			// ss->cb_save.write_space(sk);
		} else
			assure(ss);
	} else
		assure(sk);
}

/*
 *  ERROR Available
 *  -------------------------------------------------------------------------
 */
STATIC void _ss_sock_error_report(long data)
{
	struct sock *sk;
	ss_t *ss;
	if ((sk = (struct sock *) data)) {
		if ((ss = SOCK_PRIV(sk))) {
			read_lock(&sk->callback_lock);
			ss_putctl(ss, ss->rq, M_ERROR, &_ss_sock_error_report, sk);
			read_unlock(&sk->callback_lock);
		} else
			assure(ss);
	} else
		assure(sk);
}
STATIC void ss_error_report(struct sock *sk)
{
	if (sk) {
		ss_t *ss;
		if ((ss = SOCK_PRIV(sk))) {
			_ss_sock_error_report((long) sk);
			// ss->cb_save.error_report(sk);
		} else
			assure(ss);
	} else
		assure(sk);
}

/*
 *  READ Available
 *  -------------------------------------------------------------------------
 */
STATIC void _ss_sock_data_ready(long data)
{
	struct sock *sk;
	ss_t *ss;
	if ((sk = (struct sock *) data)) {
		if ((ss = SOCK_PRIV(sk))) {
			read_lock(&sk->callback_lock);
			ss_putctl(ss, ss->rq, M_READ, &_ss_sock_data_ready, sk);
			read_unlock(&sk->callback_lock);
		} else
			assure(ss);
	} else
		assure(sk);
}
STATIC void ss_data_ready(struct sock *sk, int len)
{
	if (sk) {
		ss_t *ss;
		if ((ss = SOCK_PRIV(sk))) {
			(void) len;
			_ss_sock_data_ready((long) sk);
			// ss->cb_save.data_ready(sk, len);
		} else
			assure(ss);
	} else
		assure(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)
{
	int err = -EFAULT;
	ss_t *ss = PRIV(q);
	const struct T_conn_req *p = (typeof(p)) mp->b_rptr;
	if (ss->p.info.SERV_type == T_CLTS)
		goto notsupport;
	if (ss_get_state(ss) != TS_IDLE)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p)
	    || mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length ||
	    mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
		goto einval;
	else {
		ss_addr_t *dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
		if (p->DEST_length < sizeof(*dst))
			goto badaddr;
		if (dst->sin_port == 0)
			goto badaddr;
		if (dst->sin_family != ss->p.prot.family)
			goto badaddr;
		if (ss->cred.cr_uid != 0 && htons(dst->sin_port) == IPPROTO_RAW)
			goto acces;
		else {
			unsigned char *opt = mp->b_rptr + p->OPT_offset;
			size_t opt_len = p->OPT_length;
			struct ss_opts opts = { 0L, NULL, };
			if (opt_len && ss_parse_opts(ss, &opts, opt, opt_len))
				goto badopt;
			/* 
			 * TODO: set options first 
			 */
			ss->dst = *dst;
			if (mp->b_cont) {
				putbq(q, mp->b_cont);	/* hold back data */
				mp->b_cont = NULL;	/* abosrbed mp->b_cont */
			}
			ss_set_state(ss, TS_WACK_CREQ);
			return t_ok_ack(q, T_CONN_REQ, NULL, NULL);
		}
	}
      badopt:
	err = TBADOPT;
	ptrace(("%s: ERROR: bad options\n", SS_MOD_NAME));
	goto error;
      acces:
	err = TACCES;
	ptrace(("%s: ERROR: no permission for address\n", SS_MOD_NAME));
	goto error;
      badaddr:
	err = TBADADDR;
	ptrace(("%s: ERROR: address is unusable\n", SS_MOD_NAME));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("%s: ERROR: invalid message format\n", SS_MOD_NAME));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME));
	goto error;
      notsupport:
	err = TNOTSUPPORT;
	ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME));
	goto error;
      error:
	return t_error_ack(q, T_CONN_REQ, err);
}

/*
 *  T_CONN_RES           1 - Accept previous connection indication
 *  -------------------------------------------------------------------
 */
STATIC mblk_t *t_seq_check(ss_t * ss, ulong seq)
{
	mblk_t *mp;
	lis_spin_lock(&ss->conq.q_lock); {
		for (mp = bufq_head(&ss->conq); mp && mp != (mblk_t *) seq; mp = mp->b_next) ;
	}
	lis_spin_unlock(&ss->conq.q_lock);
	usual(mp);
	return (mp);
}
STATIC ss_t *t_tok_check(ulong acceptor)
{
	ss_t *as;
	queue_t *aq = (queue_t *) acceptor;
	for (as = ss_opens; as && as->rq != aq; as = as->next) ;
	usual(as);
	return (as);
}
STATIC int t_conn_res(queue_t * q, mblk_t * mp)
{
	ss_t *ss = PRIV(q);
	int err = 0;
	mblk_t *cp;
	ss_t *as;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_conn_res *p = (typeof(p)) mp->b_rptr;
	struct ss_opts opts = { 0L, NULL, };
	size_t opt_len, opt_off;
	unsigned char *opt;
	if (ss->p.info.SERV_type == T_CLTS)
		goto notsupport;
	if (ss_get_state(ss) != TS_WRES_CIND)
		goto outstate;
	ss_set_state(ss, TS_WACK_CRES);
	if (mlen < sizeof(*p))
		goto inval;
	if ((cp = t_seq_check(ss, p->SEQ_number)) == NULL)
		goto badseq;
	if (((as = t_tok_check(p->ACCEPTOR_id)) == NULL)
	    || !(as == ss || ((1 << ss_get_state(as)) & TSM_DISCONN)))
		goto badf;
	if (ss->p.prot.protocol != as->p.prot.protocol)
		goto provmismatch;
	if (ss_get_state(as) == TS_IDLE && as->conind)
		goto resqlen;
	if (ss->cred.cr_uid != 0 && as->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 = ss_parse_opts(ss, &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, cp, as);
      baddata:
	err = TBADDATA;
	ptrace(("%s: ERROR: bad connection data\n", SS_MOD_NAME));
	goto error;
      badopt:
	err = TBADOPT;
	ptrace(("%s: ERROR: bad options\n", SS_MOD_NAME));
	goto error;
      acces:
	err = TACCES;
	ptrace(("%s: ERROR: no access to accepting queue\n", SS_MOD_NAME));
	goto error;
      resqlen:
	err = TRESQLEN;
	ptrace(("%s: ERROR: accepting queue is listening\n", SS_MOD_NAME));
	goto error;
      provmismatch:
	err = TPROVMISMATCH;
	ptrace(("%s: ERROR: not same transport provider\n", SS_MOD_NAME));
	goto error;
      badf:
	err = TBADF;
	ptrace(("%s: ERROR: accepting queue id is invalid\n", SS_MOD_NAME));
	goto error;
      badseq:
	err = TBADSEQ;
	ptrace(("%s: ERROR: sequence number is invalid\n", SS_MOD_NAME));
	goto error;
      inval:
	err = -EINVAL;
	ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME));
	goto error;
      notsupport:
	err = TNOTSUPPORT;
	ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME));
	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)
{
	ss_t *ss = PRIV(q);
	int err;
	mblk_t *cp = NULL;
	struct T_discon_req *p = (typeof(p)) mp->b_rptr;
	if (ss->p.info.SERV_type == T_CLTS)
		goto notsupport;
	if ((1 << ss_get_state(ss)) & ~TSM_CONNECTED)
		goto outstate;
	if (mp->b_wptr - mp->b_rptr < sizeof(*p))
		goto einval;
	switch (ss_get_state(ss)) {
	case TS_WCON_CREQ:
		ss_set_state(ss, TS_WACK_DREQ6);
		break;
	case TS_WRES_CIND:
		if ((cp = t_seq_check(ss, p->SEQ_number))) {
			ss_set_state(ss, TS_WACK_DREQ7);
			break;
		}
		goto badseq;
	case TS_DATA_XFER:
		ss_set_state(ss, TS_WACK_DREQ9);
		break;
	case TS_WIND_ORDREL:
		ss_set_state(ss, TS_WACK_DREQ10);
		break;
	case TS_WREQ_ORDREL:
		ss_set_state(ss, TS_WACK_DREQ11);
		break;
	}
	return t_ok_ack(q, T_DISCON_REQ, cp, NULL);
      badseq:
	err = TBADSEQ;
	ptrace(("%s: ERROR: sequence number is invalid\n", SS_MOD_NAME));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME));
	goto error;
      notsupport:
	err = TNOTSUPPORT;
	ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME));
	goto error;
      error:
	return t_error_ack(q, T_DISCON_REQ, err);
}

/*
 *  M_DATA
 *  -------------------------------------------------------------------
 */
STATIC int ss_w_data(queue_t * q, mblk_t * mp)
{
	int flags, mlen, mmax;
	ss_t *ss = PRIV(q);
	if (ss->p.info.SERV_type == T_CLTS)
		goto notsupport;
	if (ss_get_state(ss) == TS_IDLE)
		goto discard;
	if ((1 << ss_get_state(ss)) & ~TSM_OUTDATA)
		goto outstate;
	mlen = msgdsize(mp);
	mmax = ss->p.info.TIDU_size;
	if (mmax < ss->p.info.TSDU_size && ss->p.info.TSDU_size != T_INVALID)
		mmax = ss->p.info.ETSDU_size;
	if (mlen > mmax)
		goto emsgsize;
	flags = (ss->p.prot.type == SOCK_SEQPACKET) ? MSG_EOR : 0;
	return ss_sock_sendmsg(ss, mp, NULL, flags, NULL);
      emsgsize:
	ptrace(("%s: ERROR: message too large %d > %d\n", SS_MOD_NAME, mlen, mmax));
	goto error;
      outstate:
	ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME));
	goto error;
      discard:
	ptrace(("%s: ERROR: ignore in idle state\n", SS_MOD_NAME));
	return (QR_DONE);
      notsupport:
	ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME));
	goto error;
      error:
	return m_error(q, EPROTO);
}

/*
 *  T_DATA_REQ           3 - Connection-Mode data transfer request
 *  -------------------------------------------------------------------
 */
STATIC int t_data_req(queue_t * q, mblk_t * mp)
{
	int flags, mlen, mmax;
	ss_t *ss = PRIV(q);
	const struct T_data_req *p = (typeof(p)) mp->b_rptr;
	if (ss->p.info.SERV_type == T_CLTS)
		goto notsupport;
	if (ss_get_state(ss) == TS_IDLE)
		goto discard;
	if (mp->b_wptr - mp->b_rptr < sizeof(*p))
		goto einval;
	if ((1 << ss_get_state(ss)) & ~TSM_OUTDATA)
		goto outstate;
	mlen = msgdsize(mp);
	mmax = ss->p.info.TIDU_size;
	if (mmax < ss->p.info.TSDU_size && ss->p.info.TSDU_size != T_INVALID)
		mmax = ss->p.info.ETSDU_size;
	if (mlen > mmax)
		goto emsgsize;
	flags = (ss->p.prot.type == SOCK_SEQPACKET && !p->MORE_flag) ? MSG_EOR : 0;
	return ss_sock_sendmsg(ss, mp, NULL, flags, NULL);
      emsgsize:
	ptrace(("%s: ERROR: message too large %d > %d\n", SS_MOD_NAME, mlen, mmax));
	goto error;
      outstate:
	ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME));
	goto error;
      einval:
	ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME));
	goto error;
      discard:
	ptrace(("%s: ERROR: ignore in idle state\n", SS_MOD_NAME));
	return (QR_DONE);
      notsupport:
	ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME));
	goto error;
      error:
	return m_error(q, EPROTO);
}

/*
 *  T_EXDATA_REQ         4 - Expedited data request
 *  -------------------------------------------------------------------
 */
STATIC int t_exdata_req(queue_t * q, mblk_t * mp)
{
	int flags, mlen, mmax;
	ss_t *ss = PRIV(q);
	const struct T_exdata_req *p = (typeof(p)) mp->b_rptr;
	if (ss->p.info.SERV_type == T_CLTS)
		goto notsupport;
	if (ss_get_state(ss) == TS_IDLE)
		goto discard;
	if (mp->b_wptr - mp->b_rptr < sizeof(*p))
		goto einval;
	if ((1 << ss_get_state(ss)) & ~TSM_OUTDATA)
		goto outstate;
	mlen = msgdsize(mp);
	mmax = ss->p.info.TIDU_size;
	if (mmax < ss->p.info.ETSDU_size && ss->p.info.ETSDU_size != T_INVALID)
		mmax = ss->p.info.ETSDU_size;
	if (mlen > mmax)
		goto emsgsize;
	flags = (ss->p.prot.type == SOCK_SEQPACKET && !p->MORE_flag) ? MSG_EOR : 0;
	return ss_sock_sendmsg(ss, mp, NULL, MSG_OOB | flags, NULL);
      emsgsize:
	ptrace(("%s: ERROR: message too large %d > %d\n", SS_MOD_NAME, mlen, mmax));
	goto error;
      outstate:
	ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME));
	goto error;
      einval:
	ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME));
	goto error;
      discard:
	ptrace(("%s: ERROR: ignore in idle state\n", SS_MOD_NAME));
	return (QR_DONE);
      notsupport:
	ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME));
	goto error;
      error:
	return m_error(q, EPROTO);
}

/*
 *  T_INFO_REQ           5 - Information Request
 *  -------------------------------------------------------------------
 */
STATIC int t_info_req(queue_t * q, mblk_t * mp)
{
	ss_t *ss = PRIV(q);
	(void) mp;
	(void) ss;
	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)
{
	ss_t *ss = PRIV(q);
	int err;
	const struct T_bind_req *p = (typeof(p)) mp->b_rptr;
	if (ss_get_state(ss) != TS_UNBND)
		goto outstate;
	ss_set_state(ss, TS_WACK_BREQ);
	if (mp->b_wptr - mp->b_rptr < sizeof(*p))
		goto einval;
	if (ss->p.info.SERV_type == T_CLTS && p->CONIND_number)
		goto notsupport;
	if ((mp->b_wptr - mp->b_rptr < p->ADDR_offset + p->ADDR_length)
	    || (p->ADDR_length != sizeof(ss_addr_t)))
		goto badaddr;
	{
		ss_addr_t *add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
		/* 
		 * we don't allow wildcards yet 
		 */
		if (!add->sin_port)
			goto noaddr;
		if (add->sin_family != ss->p.prot.family)
			goto badaddr;
		if (ss->p.prot.type == SOCK_RAW && ss->cred.cr_uid != 0)
			goto acces;
		ss->port = htons(add->sin_port);
		if ((err = ss_socket(ss)))
			goto error;
		if ((err = ss_bind(ss, add)))
			goto error_close;
		ss->conind = 0;
		if (p->CONIND_number && (ss->p.prot.type == SOCK_STREAM || ss->p.prot.type == SOCK_SEQPACKET))
			if ((err = ss_listen(ss, p->CONIND_number)))
				goto error_close;
		return t_bind_ack(q, &ss->src, p->CONIND_number);
	}
      acces:
	err = TACCES;
	ptrace(("%s: ERROR: no permission for address\n", SS_MOD_NAME));
	goto error;
      noaddr:
	err = TNOADDR;
	ptrace(("%s: ERROR: couldn't allocate address\n", SS_MOD_NAME));
	goto error;
      badaddr:
	err = TBADADDR;
	ptrace(("%s: ERROR: address is invalid\n", SS_MOD_NAME));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME));
	goto error;
      notsupport:
	err = TNOTSUPPORT;
	ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME));
	goto error;
      error_close:
	ss_socket_put(xchg(&ss->sock, NULL));
      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)
{
	ss_t *ss = PRIV(q);
	if (ss_get_state(ss) != TS_IDLE)
		goto outstate;
	ss_set_state(ss, TS_WACK_UREQ);
	return t_ok_ack(q, T_UNBIND_REQ, NULL, NULL);
      outstate:
	ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME));
	return t_error_ack(q, T_UNBIND_REQ, TOUTSTATE);
}

/*
 *  T_UNITDATA_REQ       8 -Unitdata Request 
 *  -------------------------------------------------------------------
 */
STATIC int t_unitdata_req(queue_t * q, mblk_t * mp)
{
	ss_t *ss = PRIV(q);
	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;
	if (ss->p.info.SERV_type != T_CLTS)
		goto notsupport;
	if (dlen == 0 && !(ss->p.info.PROVIDER_flag & T_SNDZERO))
		goto baddata;
	if (dlen > ss->p.info.TSDU_size || dlen > ss->p.info.TIDU_size)
		goto baddata;
	if (ss_get_state(ss) != TS_IDLE)
		goto outstate;
	if (mlen < sizeof(*p))
		goto einval;
	if ((mlen < p->DEST_offset + p->DEST_length) || (p->DEST_length != sizeof(ss_addr_t)))
		goto badadd;
	if (ss->p.prot.type == SOCK_RAW && ss->cred.cr_uid != 0)
		goto acces;
	else {
		size_t opt_len = p->OPT_length;
		size_t opt_off = p->OPT_offset;
		unsigned char *opt = mp->b_rptr + opt_off;
		struct ss_opts opts = { 0L, NULL, };
		if (opt_len && (mlen < opt_off + opt_len || ss_parse_opts(ss, &opts, opt, opt_len)))
			goto badopt;
		else {
			int flags = (opts.norte && *(opts.norte)) ? MSG_DONTROUTE : 0;
			ss_addr_t *d = (typeof(d)) (mp->b_rptr + p->DEST_offset);
			return ss_sock_sendmsg(ss, mp, d, flags, &opts);
		}
	}
      badopt:
	ptrace(("%s: ERROR: bad options\n", SS_MOD_NAME));
	goto error;
      acces:
	ptrace(("%s: ERROR: no permission to address\n", SS_MOD_NAME));
	goto error;
      badadd:
	ptrace(("%s: ERROR: bad destination address\n", SS_MOD_NAME));
	goto error;
      einval:
	ptrace(("%s: ERROR: invalid primitive\n", SS_MOD_NAME));
	goto error;
      outstate:
	ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME));
	goto error;
      baddata:
	ptrace(("%s: ERROR: bad data size\n", SS_MOD_NAME));
	goto error;
      notsupport:
	ptrace(("%s: ERROR: primitive not supported for T_COTS\n", SS_MOD_NAME));
	goto error;
      error:
	return m_error(q, EPROTO);
}

/*
 *  T_OPTMGMT_REQ        9 - Options management request
 *  -------------------------------------------------------------------
 */
STATIC int t_optmgmt_req(queue_t * q, mblk_t * mp)
{
	ss_t *ss = 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 ss_opts opts = { 0L, NULL, };
	size_t opt_len, opt_off;
	unsigned char *opt;
#ifdef TS_WACK_OPTREQ
	if (ss_get_state(ss) == TS_IDLE)
		ss_set_state(ss, 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 = ss_parse_opts(ss, &opts, opt, opt_len))))
		goto badopt;
	switch (p->MGMT_flags) {
	case T_CHECK:
		err = ss_opt_check(ss, &opts);
		break;
	case T_NEGOTIATE:
		if (!opts.flags)
			ss_opt_default(ss, &opts);
		else if ((err = ss_opt_check(ss, &opts)))
			break;
		err = ss_opt_negotiate(ss, &opts);
		break;
	case T_DEFAULT:
		err = ss_opt_default(ss, &opts);
		break;
	case T_CURRENT:
		err = ss_opt_current(ss, &opts);
		break;
	default:
		goto badflag;
	}
	if (err)
		goto provspec;
	return t_optmgmt_ack(q, p->MGMT_flags, &opts);
      provspec:
	err = err;
	ptrace(("%s: ERROR: provider specific\n", SS_MOD_NAME));
	goto error;
      badflag:
	err = TBADFLAG;
	ptrace(("%s: ERROR: bad options flags\n", SS_MOD_NAME));
	goto error;
      badopt:
	err = TBADOPT;
	ptrace(("%s: ERROR: bad options\n", SS_MOD_NAME));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME));
	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)
{
	ss_t *ss = PRIV(q);
	const struct T_ordrel_req *p = (typeof(p)) mp->b_rptr;
	if (ss->p.info.SERV_type != T_COTS_ORD)
		goto notsupport;
	if ((1 << ss_get_state(ss)) & ~TSM_OUTDATA)
		goto outstate;
	ss->sock->ops->shutdown(ss->sock, SEND_SHUTDOWN);
	switch (ss_get_state(ss)) {
	case TS_DATA_XFER:
		ss_set_state(ss, TS_WIND_ORDREL);
		break;
	case TS_WREQ_ORDREL:
		ss_set_state(ss, TS_IDLE);
		break;
	}
	return (QR_DONE);
      outstate:
	ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME));
	goto error;
      notsupport:
	ptrace(("%s: ERROR: primitive not supported for T_CLTS or T_COTS\n", SS_MOD_NAME));
	goto error;
      error:
	return m_error(q, EPROTO);
}

/*
 *  T_OPTDATA_REQ       24 - Data with options request
 *  -------------------------------------------------------------------
 */
STATIC int t_optdata_req(queue_t * q, mblk_t * mp)
{
	int err;
	ss_t *ss = PRIV(q);
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_optdata_req *p = (typeof(p)) mp->b_rptr;
	if (ss->p.info.SERV_type == T_CLTS)
		goto notsupport;
	if (ss_get_state(ss) == TS_IDLE)
		goto discard;
	if (mlen < sizeof(*p))
		goto einval;
	if ((1 << ss_get_state(ss)) & ~TSM_OUTDATA)
		goto outstate;
	else {
		struct ss_opts opts = { 0L, NULL, };
		size_t opt_len = p->OPT_length;
		size_t opt_off = p->OPT_offset;
		unsigned char *opt = mp->b_rptr + opt_off;
		if (opt_len && (mlen < opt_off + opt_len || ss_parse_opts(ss, &opts, opt, opt_len)))
			goto badopt;
		else {
			int 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) && ss->p.prot.type == SOCK_SEQPACKET)
				flags |= MSG_EOR;
			return ss_sock_sendmsg(ss, mp, NULL, flags, &opts);
		}
	}
      badopt:
	err = TBADOPT;
	ptrace(("%s: ERROR: bad options\n", SS_MOD_NAME));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("%s: ERROR: would place i/f out of state\n", SS_MOD_NAME));
	goto error;
      einval:
	ptrace(("%s: ERROR: invalid primitive format\n", SS_MOD_NAME));
	return m_error(q, EPROTO);
      discard:
	ptrace(("%s: ERROR: ignore in idle state\n", SS_MOD_NAME));
	return (QR_DONE);
      notsupport:
	err = TNOTSUPPORT;
	ptrace(("%s: ERROR: primitive not supported for T_CLTS\n", SS_MOD_NAME));
	goto error;
      error:
	return t_error_ack(q, T_OPTDATA_REQ, err);
}

#ifdef T_ADDR_REQ
/*
 *  T_ADDR_REQ          25 - Address Request
 *  -------------------------------------------------------------------
 */
STATIC int t_addr_req(queue_t * q, mblk_t * mp)
{
	ss_t *ss = PRIV(q);
	(void) mp;
	switch (ss_get_state(ss)) {
	case TS_UNBND:
		return t_addr_ack(q, NULL, NULL);
	case TS_IDLE:
		return t_addr_ack(q, &ss->src, NULL);
	case TS_WCON_CREQ:
	case TS_DATA_XFER:
	case TS_WIND_ORDREL:
	case TS_WREQ_ORDREL:
		return t_addr_ack(q, &ss->src, &ss->dst);
	case TS_WRES_CIND:
		return t_addr_ack(q, NULL, &ss->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);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  IO Controls
 *
 *  -------------------------------------------------------------------------
 */
#if 0
STATIC int ti_getinfo(queue_t * q, mblk_t * mp)
{
}
STATIC int ti_optmgmt(queue_t * q, mblk_t * mp)
{
}
STATIC int ti_bind(queue_t * q, mblk_t * mp)
{
}
STATIC int ti_unbind(queue_t * q, mblk_t * mp)
{
}
STATIC int ti_getmyname(queue_t * q, mblk_t * mp)
{
}
STATIC int ti_getpeername(queue_t * q, mblk_t * mp)
{
}
STATIC int ti_setmyname(queue_t * q, mblk_t * mp)
{
}
STATIC int ti_setpeername(queue_t * q, mblk_t * mp)
{
}
#endif

/*
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 *  There are a standard set of IOCTLs for TLI.  Check the iBCS package for
 *  the numbering.
 */
STATIC int ss_w_ioctl(queue_t * q, mblk_t * mp)
{
	(void) q;
	(void) mp;
	fixme(("Support finalized IOCTLs\n"));
#if 0
	/* these are supported by timod which can be pushed */
	switch (nr) {
	case _IOC_NR(TI_GETINFO):
		rtn = ti_getinfo(q, mp);
		break;
	case _IOC_NR(TI_OPTMGMT):
		rtn = ti_optmgmt(q, mp);
		break;
	case _IOC_NR(TI_BIND):
		rtn = ti_bind(q, mp);
		break;
	case _IOC_NR(TI_UNBIND):
		rtn = ti_unbind(q, mp);
		break;
	case _IOC_NR(TI_GETMYNAME):
		rtn = ti_getmyname(q, mp);
		break;
	case _IOC_NR(TI_GETPEERNAME):
		rtn = ti_getpeername(q, mp);
		break;
	case _IOC_NR(TI_SETMYNAME):
		rtn = ti_setmyname(q, mp);
		break;
	case _IOC_NR(TI_SETPEERNAME):
		rtn = ti_setpeername(q, mp);
		break;
	default:
		rtn = -EOPNOTSUPP;
		break;
	}
#endif
	return (-EOPNOTSUPP);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int ss_w_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	ss_t *ss = PRIV(q);
	ulong oldstate = ss_get_state(ss);
	switch ((prim = *((ulong *) mp->b_rptr))) {
	case T_CONN_REQ:
		printd(("%s: %p: -> T_CONN_REQ\n", SS_MOD_NAME, ss));
		rtn = t_conn_req(q, mp);
		break;
	case T_CONN_RES:
		printd(("%s: %p: -> T_CONN_RES\n", SS_MOD_NAME, ss));
		rtn = t_conn_res(q, mp);
		break;
	case T_DISCON_REQ:
		printd(("%s: %p: -> T_DISCON_REQ\n", SS_MOD_NAME, ss));
		rtn = t_discon_req(q, mp);
		break;
	case T_DATA_REQ:
		printd(("%s: %p: -> T_DATA_REQ\n", SS_MOD_NAME, ss));
		rtn = t_data_req(q, mp);
		break;
	case T_EXDATA_REQ:
		printd(("%s: %p: -> T_EXDATA_REQ\n", SS_MOD_NAME, ss));
		rtn = t_exdata_req(q, mp);
		break;
	case T_INFO_REQ:
		printd(("%s: %p: -> T_INFO_REQ\n", SS_MOD_NAME, ss));
		rtn = t_info_req(q, mp);
		break;
	case T_BIND_REQ:
		printd(("%s: %p: -> T_BIND_REQ\n", SS_MOD_NAME, ss));
		rtn = t_bind_req(q, mp);
		break;
	case T_UNBIND_REQ:
		printd(("%s: %p: -> T_UNBIND_REQ\n", SS_MOD_NAME, ss));
		rtn = t_unbind_req(q, mp);
		break;
	case T_OPTMGMT_REQ:
		printd(("%s: %p: -> T_OPTMGMT_REQ\n", SS_MOD_NAME, ss));
		rtn = t_optmgmt_req(q, mp);
		break;
	case T_UNITDATA_REQ:
		printd(("%s: %p: -> T_UNITDATA_REQ\n", SS_MOD_NAME, ss));
		rtn = t_unitdata_req(q, mp);
		break;
	case T_ORDREL_REQ:
		printd(("%s: %p: -> T_ORDREL_REQ\n", SS_MOD_NAME, ss));
		rtn = t_ordrel_req(q, mp);
		break;
	case T_OPTDATA_REQ:
		printd(("%s: %p: -> T_OPTDATA_REQ\n", SS_MOD_NAME, ss));
		rtn = t_optdata_req(q, mp);
		break;
#ifdef T_ADDR_REQ
	case T_ADDR_REQ:
		printd(("%s: %p: -> T_ADDR_REQ\n", SS_MOD_NAME, ss));
		rtn = t_addr_req(q, mp);
		break;
#endif
#ifdef T_CAPABILITY_REQ
	case T_CAPABILITY_REQ:
		printd(("%s: %p: -> T_CAPABILITY_REQ\n", SS_MOD_NAME, ss));
		rtn = t_capability_req(q, mp);
		break;
#endif
	default:
		rtn = t_other_req(q, mp);
		break;
	}
	if (rtn < 0) {
		rare();
		ss_set_state(ss, oldstate);
	}
	return (rtn);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int ss_w_flush(queue_t * q, mblk_t * mp)
{
	if (*mp->b_rptr & FLUSHW) {
		if (*mp->b_rptr & FLUSHBAND)
			flushband(q, mp->b_rptr[1], FLUSHALL);
		else
			flushq(q, FLUSHALL);
		*mp->b_rptr &= ~FLUSHW;
	}
	if (*mp->b_rptr & FLUSHR) {
		if (*mp->b_rptr & FLUSHBAND)
			flushband(OTHER(q), mp->b_rptr[1], FLUSHALL);
		else
			flushq(OTHER(q), FLUSHALL);
		qreply(q, mp);
		return (QR_ABSORBED);
	}
	return (QR_DONE);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_PCRSE Handling
 *
 *  -------------------------------------------------------------------------
 *  On the read queue, an M_PCRSE is used to indicate a state_change callback
 *  on the socket.  This can mean that the socket has completed connecting or
 *  has disconnected, or it may signal a connection indication on a listening
 *  socket.
 *
 *  We always examine both the state of the TPI interface and the state of the
 *  socket to determine which actions need to be performed.  This makes the
 *  state machine interworking more robust.
 *
 *  Note that the sk pointer passed in here is not necessarily the same as
 *  ss->sock->sk, sk may be a child (connection indication) of the primary
 *  socket.
 */
STATIC int ss_r_pcrse(queue_t * q, mblk_t * mp)
{
	ss_t *ss = PRIV(q);
	ss_event_t *p = (typeof(p)) mp->b_rptr;
	int oldstate = xchg(&ss->tcp_state, p->state);
	if (!ss->sock)
		goto discard;
	assure(p->state != oldstate);
	printd(("%s: %p: state_change [%s <- %s] %p\n", SS_MOD_NAME, ss, tcp_state_name(p->state),
		tcp_state_name(oldstate), p->sk));
	switch (ss->p.prot.type) {
	case SOCK_STREAM:
	case SOCK_SEQPACKET:
		switch (ss_get_state(ss)) {
		case TS_WCON_CREQ:
			switch (p->state) {
			case TCP_ESTABLISHED:
				return (t_conn_con(q, &ss->dst, NULL, NULL));
			case TCP_TIME_WAIT:
			case TCP_CLOSE:
				return (t_discon_ind(q, &ss->dst, T_PROVIDER, 0, NULL, NULL));
			}
			break;
		case TS_WIND_ORDREL:
			switch (p->state) {
			case TCP_TIME_WAIT:
			case TCP_CLOSE:
				switch (oldstate) {
				case TCP_FIN_WAIT2:
					return (t_ordrel_ind(q));
				case TCP_ESTABLISHED:
				case TCP_FIN_WAIT1:
					return (t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
				}
				break;
			case TCP_FIN_WAIT1:
			case TCP_FIN_WAIT2:
				return (QR_DONE);
			}
			break;
		case TS_DATA_XFER:
			switch (p->state) {
			case TCP_CLOSE_WAIT:
				return (t_ordrel_ind(q));
			case TCP_TIME_WAIT:
			case TCP_CLOSE:
				return (t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
			}
			break;
		case TS_WREQ_ORDREL:
			switch (p->state) {
			case TCP_TIME_WAIT:
			case TCP_CLOSE:
				switch (oldstate) {
				case TCP_CLOSE_WAIT:
					return (t_discon_ind(q, NULL, T_PROVIDER, 0, NULL, NULL));
				}
				break;
			}
			break;
		case TS_IDLE:
		case TS_WRES_CIND:
			switch (p->state) {
			case TCP_LAST_ACK:
				switch (oldstate) {
				case TCP_CLOSE_WAIT:
					return (QR_DONE);
				}
				break;
			case TCP_TIME_WAIT:
			case TCP_CLOSE:
				switch (oldstate) {
				case TCP_FIN_WAIT2:
				case TCP_LAST_ACK:
					return (QR_DONE);
				case TCP_LISTEN:
				{
					mblk_t *cp;
					/* state change was on child */
					ss->tcp_state = TCP_LISTEN;
					/* look for the child */
					if ((cp = t_seq_find(ss, mp)))
						return (t_discon_ind(q, NULL, T_PROVIDER, 0, cp, NULL));
					return (QR_DONE);
				}
				}
				break;
			case TCP_ESTABLISHED:
				switch (oldstate) {
				case TCP_LISTEN:
					/* state change was on child */
					ss->tcp_state = TCP_LISTEN;
					return (ss_conn_ind(q, mp));
				}
				break;
			}
			break;
		}
		printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss,
			tcp_state_name(p->state), state_name(ss_get_state(ss))));
		return (-EFAULT);
	case SOCK_DGRAM:
	case SOCK_RAW:
	case SOCK_RDM:
		printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss,
			tcp_state_name(p->state), state_name(ss_get_state(ss))));
		return (-EFAULT);
	default:
		printd(("%s: SWERR: unsupported socket type\n", SS_MOD_NAME));
		return (-EFAULT);
	}
      discard:
	printd(("%s: ERROR: lingering event, ignoring\n", SS_MOD_NAME));
	return (QR_DONE);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_READ Handling
 *
 *  -------------------------------------------------------------------------
 *
 *  M_READ on the RD(q)
 *  -------------------------------------------------------------------------
 *
 *  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), or it may mean that a socket has completed connecting, or
 *  disconnected, or that a connection indication may be availabel on a
 *  listening socket.
 *
 *  We always examine both the state of the TPI interface and the state of the
 *  socket to determine which actions need to be performed.  This makes the
 *  state machine interworking more robust.
 *
 *  Note that the sk pointer passed in here is not necessarily the same as
 *  ss->sock->sk, sk may be a child (connection indication) of the primary
 *  socket.
 */
STATIC int ss_r_read(queue_t * q, mblk_t * mp)
{
	ss_t *ss = PRIV(q);
	ss_event_t *p = (typeof(p)) mp->b_rptr;
	if (!ss->sock)
		goto discard;
	assure(ss->tcp_state == p->state || ss->tcp_state == TCP_LISTEN);
	printd(("%s: %p: data_ready [%s <- %s] %p\n", SS_MOD_NAME, ss, tcp_state_name(p->state),
		tcp_state_name(ss->tcp_state), p->sk));
	switch (ss->p.prot.type) {
	case SOCK_STREAM:	/* TCP */
	case SOCK_SEQPACKET:	/* SCTP */
		switch (ss_get_state(ss)) {
		case TS_IDLE:
		case TS_WRES_CIND:
			if (p->state == TCP_LISTEN)
				return (QR_DONE);
		case TS_DATA_XFER:
		case TS_WIND_ORDREL:
		case TS_WREQ_ORDREL:	/* TCP bug I believe */
			return ss_sock_recvmsg(q);
		}
		printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss,
			tcp_state_name(p->state), state_name(ss_get_state(ss))));
		return (-EFAULT);
	case SOCK_DGRAM:
	case SOCK_RAW:
	case SOCK_RDM:
		switch (ss_get_state(ss)) {
		case TS_IDLE:
			return ss_sock_recvmsg(q);
		}
		printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss,
			tcp_state_name(p->state), state_name(ss_get_state(ss))));
		return (-EFAULT);
	}
	printd(("%s: SWERR: unsupported socket type %d\n", SS_MOD_NAME, ss->p.prot.type));
	return (-EFAULT);
      discard:
	printd(("%s: ERROR: lingering event, ignoring\n", SS_MOD_NAME));
	return (QR_DONE);
}

/*
 *  M_READ on the WR(q)
 *  -------------------------------------------------------------------------
 *
 *  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).  This can also mean that a connecting socket has
 *  completed connecting (or has disconnected), or that a listening socket has
 *  a connection indication on the socket.
 *
 *  We always examine both the state of the TPI interface and the state of the
 *  socket to determine which actions need to be performed.  This makes the
 *  state machine interworking more robust.
 *
 *  Note that the sk pointer passed in here is not necessarily the same as
 *  ss->sock->sk, sk may be a child (connection indication) of the primary
 *  socket.
 *
 */
STATIC int ss_w_read(queue_t * q, mblk_t * mp)
{
	ss_t *ss = PRIV(q);
	ss_event_t *p = (typeof(p)) mp->b_rptr;
	if (!ss->sock)
		goto discard;
	assure(ss->tcp_state == p->state || ss->tcp_state == TCP_LISTEN);
	printd(("%s: %p: write_space [%s <- %s] %p\n", SS_MOD_NAME, ss, tcp_state_name(p->state),
		tcp_state_name(ss->tcp_state), p->sk));
	switch (ss->p.prot.type) {
	case SOCK_STREAM:
	case SOCK_SEQPACKET:
		switch (ss_get_state(ss)) {
		case TS_DATA_XFER:
		case TS_WREQ_ORDREL:
			return (QR_DONE);
		}
		printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss,
			tcp_state_name(p->state), state_name(ss_get_state(ss))));
		return (-EFAULT);
	case SOCK_DGRAM:
	case SOCK_RAW:
	case SOCK_RDM:
		switch (ss_get_state(ss)) {
		case TS_IDLE:
			return (QR_DONE);
		}
		printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss,
			tcp_state_name(p->state), state_name(ss_get_state(ss))));
		return (-EFAULT);
	}
	printd(("%s: SWERR: unsupported socket type %d\n", SS_MOD_NAME, ss->p.prot.type));
	return (-EFAULT);
      discard:
	printd(("%s: ERROR: lingering event, ignoring\n", SS_MOD_NAME));
	return (QR_DONE);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 *  On the read queue, an M_ERROR is used to indicate an error_report callback
 *  on the socket.  This can mean that the socket has disconected.  We might
 *  not received any other indication of the error.
 *
 *  We always examine both the state of the TPI interface and the state of the
 *  socket to determine which actions need to be performed.  This makes the
 *  state machine interworking more robust.
 *
 *  Note that the sk pointer passed in here is not necessarily the same as
 *  ss->sock->sk, sk may be a child (connection indication) of the primary
 *  socket.
 */
STATIC int ss_r_error(queue_t * q, mblk_t * mp)
{
	int err;
	ss_t *ss = PRIV(q);
	ss_event_t *p = (typeof(p)) mp->b_rptr;
	err = sock_error(p->sk);
	if (!ss->sock)
		goto discard;
	assure(ss->tcp_state == p->state || ss->tcp_state == TCP_LISTEN);
	printd(("%s: %p: error_report [%s <- %s] %p\n", SS_MOD_NAME, ss, tcp_state_name(p->state),
		tcp_state_name(ss->tcp_state), p->sk));
	switch (ss->p.prot.type) {
	case SOCK_STREAM:
	case SOCK_SEQPACKET:
		switch (ss_get_state(ss)) {
		default:
			fixme(("%s: %p: FIXME: save errors for later\n", SS_MOD_NAME, ss));
			return (QR_DONE);
		case TS_IDLE:
			printd(("%s: %p: INFO: ignoring error event %d\n", SS_MOD_NAME, ss, err));
			return (QR_DONE);
		}
		printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss,
			tcp_state_name(p->state), state_name(ss_get_state(ss))));
		return (-EFAULT);
	case SOCK_DGRAM:
	case SOCK_RAW:
	case SOCK_RDM:
		switch (ss_get_state(ss)) {
		case TS_IDLE:
			fixme(("%s: %p: FIXME: generate uderror\n", SS_MOD_NAME, ss));
			printd(("%s: %p: INFO: ignoring error event %d\n", SS_MOD_NAME, ss, err));
			return (QR_DONE);
		}
		printd(("%s: %p: SWERR: socket state %s in TPI state %s\n", SS_MOD_NAME, ss,
			tcp_state_name(p->state), state_name(ss_get_state(ss))));
		return (-EFAULT);
	}
	printd(("%s: SWERR: unsupported socket type %d\n", SS_MOD_NAME, ss->p.prot.type));
	return (-EFAULT);
      discard:
	printd(("%s: ERROR: lingering event, ignoring\n", SS_MOD_NAME));
	return (QR_DONE);
}

/*
 *  =========================================================================
 *
 *  QUEUE PUT and SERVICE routines
 *
 *  =========================================================================
 *
 *  WRITE PUT ad SERVICE (Message from above IP-User --> IP-Provider
 *  -------------------------------------------------------------------------
 */
STATIC int ss_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return ss_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return ss_w_proto(q, mp);
	case M_CTL:
	case M_READ:
		return ss_w_read(q, mp);
	case M_FLUSH:
		return ss_w_flush(q, mp);
	case M_IOCTL:
		return ss_w_ioctl(q, mp);
	}
	return (-EOPNOTSUPP);
}

/*
 *  READ PUT ad SERVICE (Message from below IP-Provider --> IP-User
 *  -------------------------------------------------------------------------
 */
STATIC int ss_r_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_RSE:
	case M_PCRSE:
		return ss_r_pcrse(q, mp);
	case M_CTL:
	case M_READ:
		return ss_r_read(q, mp);
	case M_ERROR:
		return ss_r_error(q, mp);
	}
	return (QR_PASSFLOW);
}

/*
 *  =========================================================================
 *
 *  QUEUE PUT and SERVICE routines
 *
 *  =========================================================================
 *
 *  PUTQ Put Routine
 *  -----------------------------------
 */
STATIC int ss_putq(queue_t * q, mblk_t * mp, int (*proc) (queue_t *, mblk_t *))
{
	int rtn = 0;
	if (mp->b_datap->db_type < QPCTL || q->q_count) {
		putq(q, mp);
		return (0);
	}
	if (ss_trylockq(q)) {
		do {
			/* 
			 * Fast Path 
			 */
			if ((rtn = (*proc) (q, mp)) == QR_DONE) {
				freemsg(mp);
				break;
			}
			switch (rtn) {
			case QR_DONE:
				freemsg(mp);
			case QR_ABSORBED:
				break;
			case QR_STRIP:
				if (mp->b_cont)
					putq(q, mp->b_cont);
			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:
				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:
			case -EBUSY:
			case -EAGAIN:
			case -ENOMEM:
				putq(q, mp);
				break;
			}
		}
		while (0);
		ss_unlockq(q);
	} else {
		putq(q, mp);
	}
	return (rtn);
}

/*
 *  SRVQ Service Routine
 *  -----------------------------------
 */
STATIC int ss_srvq(queue_t * q, int (*proc) (queue_t *, mblk_t *))
{
	int rtn = 0;
	if (ss_trylockq(q)) {
		mblk_t *mp;
		while ((mp = getq(q))) {
			/* 
			 * Fast Path 
			 */
			if ((rtn = proc(q, mp)) == QR_DONE) {
				freemsg(mp);
				continue;
			}
			switch (rtn) {
			case QR_DONE:
				freemsg(mp);
			case QR_ABSORBED:
				continue;
			case QR_STRIP:
				if (mp->b_cont)
					putbq(q, mp->b_cont);
			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(("%s: ERROR: (q dropping) %d\n", SS_MOD_NAME, rtn));
				freemsg(mp);
				continue;
			case QR_DISABLE:
				ptrace(("%s: ERROR: (q disabling)\n", SS_MOD_NAME));
				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(("%s: ERROR: (q stalled) %d\n", SS_MOD_NAME, rtn));
					putbq(q, mp);
					break;
				}
				/* 
				 *  Be careful not to put a priority message
				 *  back on the queue.
				 */
				switch (mp->b_datap->db_type) {
				case M_PCPROTO:
					mp->b_datap->db_type = M_PROTO;
					break;
				case M_PCRSE:
					mp->b_datap->db_type = M_RSE;
					break;
				case M_READ:
					mp->b_datap->db_type = M_CTL;
					break;
				default:
					ptrace(("%s: ERROR: (q dropping) %d\n", SS_MOD_NAME, rtn));
					freemsg(mp);
					continue;
				}
				mp->b_band = 255;
				putq(q, mp);
				break;
			}
			break;
		}
		ss_unlockq(q);
	}
	return (rtn);
}

STATIC INT ss_rput(queue_t * q, mblk_t * mp)
{
	return (INT) ss_putq(q, mp, &ss_r_prim);
}

STATIC INT ss_rsrv(queue_t * q)
{
	return (INT) ss_srvq(q, &ss_r_prim);
}

STATIC INT ss_wput(queue_t * q, mblk_t * mp)
{
	return (INT) ss_putq(q, mp, &ss_w_prim);
}

STATIC INT ss_wsrv(queue_t * q)
{
	return (INT) ss_srvq(q, &ss_w_prim);
}

/*
 *  =========================================================================
 *
 *  Private Structure allocation, deallocation and cache
 *
 *  =========================================================================
 */
STATIC kmem_cache_t *ss_priv_cachep = NULL;
STATIC int ss_init_caches(void)
{
	if (!ss_priv_cachep &&
	    !(ss_priv_cachep =
	      kmem_cache_create("ss_priv_cachep", sizeof(ss_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) {
		cmn_err(CE_PANIC, "%s: Cannot allocate ss_priv_cachep", __FUNCTION__);
		return (-ENOMEM);
	} else
		printd(("%s: initialized driver private structure cache\n", SS_MOD_NAME));
	return (0);
}
STATIC void ss_term_caches(void)
{
	if (ss_priv_cachep) {
		if (kmem_cache_destroy(ss_priv_cachep))
			cmn_err(CE_WARN, "%s: did not destroy ss_priv_cachep", __FUNCTION__);
		else
			printd(("%s: destroyed ss_priv_cachep\n", SS_MOD_NAME));
	}
	return;
}
STATIC ss_t *ss_alloc_priv(queue_t * q, ss_t ** slp, ushort cmajor, ushort cminor, cred_t * crp,
			   const ss_profile_t * prof)
{
	ss_t *ss;
	if ((ss = kmem_cache_alloc(ss_priv_cachep, SLAB_ATOMIC))) {
		// printd(("%s: allocated module private structure\n", SS_MOD_NAME));
		bzero(ss, sizeof(*ss));
		ss->cmajor = cmajor;
		ss->cminor = cminor;
		ss->rq = RD(q);
		ss->rq->q_ptr = ss;
		ss->refcnt++;
		ss->wq = WR(q);
		ss->wq->q_ptr = ss;
		ss->refcnt++;
		ss->cred = *crp;
		ss->p = *prof;
		lis_spin_lock_init(&ss->qlock, "ss-queue-lock");
		ss->rwait = NULL;
		ss->wwait = NULL;
		ss_set_state(ss, TS_UNBND);
		lis_spin_lock_init(&ss->lock, "ss-priv-lock");
		bufq_init(&ss->conq);
		if ((ss->next = *slp))
			ss->next->prev = &ss->next;
		ss->prev = slp;
		*slp = ss;
		ss->refcnt++;
		// printd(("%s: linked private structure, reference count %d\n", SS_MOD_NAME,
		// ss->refcnt));
	} else
		ptrace(("%s: ERROR: Could not allocate module private structure\n", SS_MOD_NAME));
	return (ss);
}
STATIC void ss_free_priv(queue_t * q)
{
	ss_t *ss = PRIV(q);
	int flags = 0;
	// printd(("%s: unlinking private structure, reference count = %d\n", SS_MOD_NAME,
	// ss->refcnt));
	lis_spin_lock_irqsave(&ss->lock, &flags); {
		bufq_purge(&ss->conq);
		if (ss->sock)
			ss_socket_put(ss->sock);
		// printd(("%s: removed socket, reference count = %d\n", SS_MOD_NAME, ss->refcnt));
		ss_unbufcall(q);
		// printd(("%s: removed bufcalls, reference count = %d\n", SS_MOD_NAME,
		// ss->refcnt));
		if ((*ss->prev = ss->next))
			ss->next->prev = ss->prev;
		ss->next = NULL;
		ss->prev = NULL;
		ss->refcnt--;
		// printd(("%s: unlinked, reference count = %d\n", SS_MOD_NAME, ss->refcnt));
		ss->rq->q_ptr = NULL;
		ss->refcnt--;
		ss->wq->q_ptr = NULL;
		ss->refcnt--;
	}
	lis_spin_unlock_irqrestore(&ss->lock, &flags);
	if (ss->refcnt) {
		assure(ss->refcnt);
		printd(("%s: WARNING: ss->refcnt = %d\n", SS_MOD_NAME, ss->refcnt));
	}
	kmem_cache_free(ss_priv_cachep, ss);
	// printd(("%s: freed module private structure\n", SS_MOD_NAME));
	return;
}

/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 *
 *  OPEN
 *  -------------------------------------------------------------------------
 */
STATIC const ss_profile_t ss_profiles[] = {
	{{PF_INET, SOCK_RAW, IPPROTO_ICMP},
	 {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID,
	  sizeof(struct sockaddr_in),
	  T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}},
	{{PF_INET, SOCK_RAW, IPPROTO_IGMP},
	 {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID,
	  sizeof(struct sockaddr_in),
	  T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}},
	{{PF_INET, SOCK_RAW, IPPROTO_IPIP},
	 {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID,
	  sizeof(struct sockaddr_in),
	  T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}},
	{{PF_INET, SOCK_STREAM, IPPROTO_TCP},
	 {T_INFO_ACK, T_INVALID, 1, T_INVALID, T_INVALID,
	  sizeof(struct sockaddr_in),
	  T_INFINITE, 0xffff, T_COTS_ORD, TS_UNBND, XPG4_1 & ~T_SNDZERO}},
	{{PF_INET, SOCK_RAW, IPPROTO_EGP},
	 {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID,
	  sizeof(struct sockaddr_in),
	  T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}},
	{{PF_INET, SOCK_RAW, IPPROTO_PUP},
	 {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID,
	  sizeof(struct sockaddr_in),
	  T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}},
	{{PF_INET, SOCK_DGRAM, IPPROTO_UDP},
	 {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID,
	  sizeof(struct sockaddr_in),
	  T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}},
	{{PF_INET, SOCK_RAW, IPPROTO_IDP},
	 {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID,
	  sizeof(struct sockaddr_in),
	  T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}},
	{{PF_INET, SOCK_RAW, IPPROTO_RAW},
	 {T_INFO_ACK, 0xffff, T_INVALID, T_INVALID, T_INVALID,
	  sizeof(struct sockaddr_in),
	  T_INFINITE, 0xffff, T_CLTS, TS_UNBND, XPG4_1 & ~T_SNDZERO}
	 }
};
STATIC int ss_majors[SS_NMAJOR] = { SS_CMAJOR, };
STATIC int ss_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	int flags, mindex = 0;
	int cmajor = getmajor(*devp);
	int cminor = getminor(*devp);
	ss_t *ss, **ipp = &ss_opens;
	const ss_profile_t *prof;
	MOD_INC_USE_COUNT;	/* keep module from unloading in our face */
	if (q->q_ptr != NULL) {
		MOD_DEC_USE_COUNT;
		return (0);	/* already open */
	}
	if (sflag == MODOPEN || WR(q)->q_next) {
		ptrace(("%s: ERROR: can't push as module\n", SS_MOD_NAME));
		MOD_DEC_USE_COUNT;
		return (EIO);
	}
	if (cmajor != SS_CMAJOR || cminor < ICMP_CMINOR || cminor > RAWIP_CMINOR) {
		MOD_DEC_USE_COUNT;
		return (ENXIO);
	}
	prof = &ss_profiles[cminor - ICMP_CMINOR];
	cminor = FREE_CMINOR;
	lis_spin_lock_irqsave(&ss_lock, &flags);
	for (; *ipp; ipp = &(*ipp)->next) {
		if (cmajor != (*ipp)->cmajor)
			break;
		if (cmajor == (*ipp)->cmajor) {
			if (cminor < (*ipp)->cminor)
				break;
			if (cminor == (*ipp)->cminor) {
				if (++cminor >= SS_NMINOR) {
					if (++mindex >= SS_NMAJOR || !(cmajor = ss_majors[mindex]))
						break;
					cminor = 0;
				}
				continue;
			}
		}
	}
	if (mindex >= SS_NMAJOR || !cmajor) {
		ptrace(("%s: ERROR: no device numbers available\n", SS_MOD_NAME));
		lis_spin_unlock_irqrestore(&ss_lock, &flags);
		MOD_DEC_USE_COUNT;
		return (ENXIO);
	}
	printd(("%s: opened character device %d:%d\n", SS_MOD_NAME, cmajor, cminor));
	*devp = makedevice(cmajor, cminor);
	if (!(ss = ss_alloc_priv(q, ipp, cmajor, cminor, crp, prof))) {
		ptrace(("%s: ERROR: No memory\n", SS_MOD_NAME));
		lis_spin_unlock_irqrestore(&ss_lock, &flags);
		MOD_DEC_USE_COUNT;
		return (ENOMEM);
	}
	lis_spin_unlock_irqrestore(&ss_lock, &flags);
	return (0);
}

/*
 *  CLOSE
 *  -------------------------------------------------------------------------
 */
STATIC int ss_close(queue_t * q, int flag, cred_t * crp)
{
	ss_t *ss = PRIV(q);
	int flags;
	(void) flag;
	(void) crp;
	(void) ss;
	printd(("%s: closing character device %d:%d\n", SS_MOD_NAME, ss->cmajor, ss->cminor));
	lis_spin_lock_irqsave(&ss_lock, &flags);
	ss_free_priv(q);
	lis_spin_unlock_irqrestore(&ss_lock, &flags);
	MOD_DEC_USE_COUNT;
	return (0);
}

/*
 *  =========================================================================
 *
 *  LiS Module Initialization
 *
 *  =========================================================================
 */
STATIC int ss_initialized = 0;
STATIC void ss_init(void)
{
	int err, mindex;
	cmn_err(CE_NOTE, SS_BANNER);	/* console splash */
	if ((err = ss_init_caches())) {
		cmn_err(CE_PANIC, "%s: ERROR: Counld not allocated caches", SS_MOD_NAME);
		ss_initialized = err;
		return;
	}
	for (mindex = 0; mindex < SS_NMAJOR; mindex++) {
		if ((err = lis_register_strdev(ss_majors[mindex], &ss_info, SS_NMINOR, SS_MOD_NAME)) < 0) {
			if (!mindex) {
				cmn_err(CE_PANIC, "%s: Can't register 1'st major %d", SS_MOD_NAME, ss_majors[0]);
				ss_term_caches();
				ss_initialized = err;
				return;
			}
			cmn_err(CE_WARN, "%s: Can't register %d'th major", SS_MOD_NAME, mindex + 1);
			ss_majors[mindex] = 0;
		} else if (mindex)
			ss_majors[mindex] = err;
	}
	lis_spin_lock_init(&ss_lock, "ss-open-list-lock");
	ss_initialized = 1;
	return;
}
STATIC void ss_terminate(void)
{
	int err, mindex;
	for (mindex = 0; mindex < SS_NMAJOR; mindex++) {
		if (ss_majors[mindex]) {
			if ((err = lis_unregister_strdev(ss_majors[mindex])))
				cmn_err(CE_PANIC, "%s: Can't unregister major %d\n", SS_MOD_NAME,
					ss_majors[mindex]);
			if (mindex)
				ss_majors[mindex] = 0;
		}
	}
	ss_term_caches();
	return;
}

/*
 *  =========================================================================
 *
 *  LINUX MODULE INITIALIZATION
 *
 *  =========================================================================
 */
int init_module(void)
{
	ss_init();
	if (ss_initialized < 0)
		return ss_initialized;
	return (0);
}
void cleanup_module(void)
{
	ss_terminate();
}


Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/inet/inet.c

OpenSS7
SS7 for the
Common Man
Home Overview Status News Documentation Resources About

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified: