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/sctp/sctp_t.c


File /code/strss7/drivers/sctp/sctp_t.c



#ident "@(#) $RCSfile: sctp_t.c,v $ $Name:  $($Revision: 0.8.2.11 $) $Date: 2003/05/30 21:15:53 $"

static char const ident[] = "$RCSfile: sctp_t.c,v $ $Name:  $($Revision: 0.8.2.11 $) $Date: 2003/05/30 21:15:53 $";

#define __NO_VERSION__

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

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

#include <sys/tpi.h>
#include <sys/tpi_sctp.h>
#include <sys/xti_ip.h>
#include <sys/xti_sctp.h>

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

#include "sctp.h"
#include "sctp_defs.h"
#include "sctp_hash.h"
#include "sctp_cache.h"
#include "sctp_msg.h"

#include "sctp_t.h"

#ifndef SCTP_T_CMAJOR
#define SCTP_T_CMAJOR 241
#endif
#define SCTP_NMINOR 255

#ifndef INT
#define INT int
#endif

typedef void (*bufcall_fnc_t) (long);

#undef min
#define min lis_min
#undef max
#define max lis_max

/*
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 *  This driver defines two user interfaces: one NPI, the other TPI.
 */

STATIC struct module_info sctp_t_minfo = {
	mi_idnum:0,				/* Module ID number */
	mi_idname:"sctp",			/* 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 sctp_t_open(queue_t *, dev_t *, int, int, cred_t *);
STATIC int sctp_t_close(queue_t *, int, cred_t *);

STATIC INT sctp_t_rput(queue_t *, mblk_t *);
STATIC INT sctp_t_rsrv(queue_t *);

STATIC struct qinit sctp_t_rinit = {
	qi_putp:sctp_t_rput,			/* Read put (msg from below) */
	qi_srvp:sctp_t_rsrv,			/* Read queue service */
	qi_qopen:sctp_t_open,			/* Each open */
	qi_qclose:sctp_t_close,			/* Last close */
	qi_minfo:&sctp_t_minfo,			/* Information */
};

STATIC INT sctp_t_wput(queue_t *, mblk_t *);
STATIC INT sctp_t_wsrv(queue_t *);

STATIC struct qinit sctp_t_winit = {
	qi_putp:sctp_t_wput,			/* Write put (msg from above) */
	qi_srvp:sctp_t_wsrv,			/* Write queue service */
	qi_minfo:&sctp_t_minfo,			/* Information */
};

STATIC struct streamtab sctp_t_info = {
	st_rdinit:&sctp_t_rinit,		/* Upper read queue */
	st_wrinit:&sctp_t_winit,		/* Upper write queue */
};

#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

/*
 *  =========================================================================
 *
 *  Option handling functions
 *
 *  =========================================================================
 */
typedef struct sctp_opts {
	uint flags;
	struct t_opthdr *bcast;			/* T_IP_BROADCAST */
	struct t_opthdr *norte;			/* T_IP_DONTROUTE */
	struct t_opthdr *opts;			/* T_IP_OPTIONS */
	struct t_opthdr *reuse;			/* T_IP_REUSEADDR */
	struct t_opthdr *tos;			/* T_IP_TOS */
	struct t_opthdr *ttl;			/* T_IP_TTL */
	struct t_opthdr *nd;			/* T_SCTP_NODELAY */
	struct t_opthdr *cork;			/* T_SCTP_CORK */
	struct t_opthdr *ppi;			/* T_SCTP_PPI */
	struct t_opthdr *sid;			/* T_SCTP_SID */
	struct t_opthdr *ssn;			/* T_SCTP_SSN */
	struct t_opthdr *tsn;			/* T_SCTP_TSN */
	struct t_opthdr *ropt;			/* T_SCTP_RECVOPT */
	struct t_opthdr *cklife;		/* T_SCTP_COOKIE_LIFE */
	struct t_opthdr *sack;			/* T_SCTP_SACK_DELAY */
	struct t_opthdr *path;			/* T_SCTP_PATH_MAX_RETRANS */
	struct t_opthdr *assoc;			/* T_SCTP_ASSOC_MAX_RETRANS */
	struct t_opthdr *init;			/* T_SCTP_MAX_INIT_RETRIES */
	struct t_opthdr *hbitvl;		/* T_SCTP_HEARTBEAT_ITVL */
	struct t_opthdr *rtoinit;		/* T_SCTP_RTO_INITIAL */
	struct t_opthdr *rtomin;		/* T_SCTP_RTO_MIN */
	struct t_opthdr *rtomax;		/* T_SCTP_RTO_MAX */
	struct t_opthdr *ostr;			/* T_SCTP_OSTREAMS */
	struct t_opthdr *istr;			/* T_SCTP_ISTREAMS */
	struct t_opthdr *ckinc;			/* T_SCTP_COOKIE_INC */
	struct t_opthdr *titvl;			/* T_SCTP_THROTTLE_ITVL */
	struct t_opthdr *hmac;			/* T_SCTP_MAC_TYPE */
	struct t_opthdr *hb;			/* T_SCTP_HB */
	struct t_opthdr *rto;			/* T_SCTP_RTO */
	struct t_opthdr *mseg;			/* T_SCTP_MAXSEG */
	struct t_opthdr *status;		/* T_SCTP_STATUS */
	struct t_opthdr *debug;			/* T_SCTP_DEBUG */
} sctp_opts_t;

/*
 *  Size and Build Default options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  Either builds the default options requested or builds all default options.
 */
STATIC size_t sctp_default_opts_size(sctp_t * sp, sctp_opts_t * ops)
{
	size_t len = 0;
	const size_t hlen = sizeof(struct t_opthdr);
	const size_t olen = hlen + sizeof(t_scalar_t);
	if (!ops || ops->bcast) {
		len += olen;
	}
	if (!ops || ops->norte) {
		len += olen;
	}
	if (!ops || ops->opts) {
		len += olen;
	}
	if (!ops || ops->reuse) {
		len += olen;
	}
	if (!ops || ops->tos) {
		len += olen;
	}
	if (!ops || ops->ttl) {
		len += olen;
	}
	if (!ops || ops->nd) {
		len += olen;
	}
	if (!ops || ops->cork) {
		len += olen;
	}
	if (!ops || ops->ppi) {
		len += olen;
	}
	if (!ops || ops->sid) {
		len += olen;
	}
	if (!ops || ops->ropt) {
		len += olen;
	}
	if (!ops || ops->cklife) {
		len += olen;
	}
	if (!ops || ops->sack) {
		len += olen;
	}
	if (!ops || ops->path) {
		len += olen;
	}
	if (!ops || ops->assoc) {
		len += olen;
	}
	if (!ops || ops->init) {
		len += olen;
	}
	if (!ops || ops->hbitvl) {
		len += olen;
	}
	if (!ops || ops->rtoinit) {
		len += olen;
	}
	if (!ops || ops->rtomin) {
		len += olen;
	}
	if (!ops || ops->rtomax) {
		len += olen;
	}
	if (!ops || ops->ostr) {
		len += olen;
	}
	if (!ops || ops->istr) {
		len += olen;
	}
	if (!ops || ops->ckinc) {
		len += olen;
	}
	if (!ops || ops->titvl) {
		len += olen;
	}
	if (!ops || ops->hmac) {
		len += olen;
	}
	if (!ops || ops->mseg) {
		len += olen;
	}
	if (!ops || ops->debug) {
		len += olen;
	}
	return (len);
}
STATIC void sctp_build_default_opts(sctp_t * sp, sctp_opts_t * ops, unsigned char **p)
{
	struct t_opthdr *oh;
	const size_t hlen = sizeof(*oh);
	const size_t olen = hlen + sizeof(t_scalar_t);

	if (!ops || ops->bcast) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_BROADCAST;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = T_NO;
	}
	if (!ops || ops->norte) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_DONTROUTE;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = T_NO;
	}
	if (!ops || ops->opts) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_OPTIONS;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = 0;
	}
	if (!ops || ops->reuse) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_REUSEADDR;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = T_NO;
	}
	if (!ops || ops->tos) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_TOS;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_ip_tos;
	}
	if (!ops || ops->ttl) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_TTL;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_ip_ttl;
	}
	if (!ops || ops->nd) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_NODELAY;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = T_YES;
	}
	if (!ops || ops->cork) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_CORK;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = T_NO;
	}
	if (!ops || ops->ppi) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_PPI;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_ppi;
	}
	if (!ops || ops->sid) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_SID;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_sid;
	}
	/* note ssn and tsn are per-packet */

	if (!ops || ops->ropt) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RECVOPT;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = T_NO;
	}
	if (!ops || ops->cklife) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_COOKIE_LIFE;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_valid_cookie_life * 1000 / HZ;
	}
	if (!ops || ops->sack) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_SACK_DELAY;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_max_sack_delay * 1000 / HZ;
	}
	if (!ops || ops->path) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_PATH_MAX_RETRANS;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_path_max_retrans;
	}
	if (!ops || ops->assoc) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_ASSOC_MAX_RETRANS;
		oh->status = T_SUCCESS;
		*((t_uscalar_t *) * p)++ = sctp_default_assoc_max_retrans;
	}
	if (!ops || ops->init) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_MAX_INIT_RETRIES;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_max_init_retries;
	}
	if (!ops || ops->hbitvl) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_HEARTBEAT_ITVL;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_heartbeat_itvl * 1000 / HZ;
	}
	if (!ops || ops->rtoinit) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RTO_INITIAL;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_rto_initial * 1000 / HZ;
	}
	if (!ops || ops->rtomin) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RTO_MIN;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_rto_min * 1000 / HZ;
	}
	if (!ops || ops->rtomax) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RTO_MAX;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_rto_max * 1000 / HZ;
	}
	if (!ops || ops->ostr) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_OSTREAMS;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_req_ostreams;
	}
	if (!ops || ops->istr) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_ISTREAMS;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_max_istreams;
	}
	if (!ops || ops->ckinc) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_COOKIE_INC;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_cookie_inc * 1000 / HZ;
	}
	if (!ops || ops->titvl) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_THROTTLE_ITVL;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_throttle_itvl * 1000 / HZ;
	}
	if (!ops || ops->hmac) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_MAC_TYPE;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sctp_default_mac_type;
	}
	if (!ops || ops->mseg) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_MAXSEG;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = 576;
	}
	if (!ops || ops->debug) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_DEBUG;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = 0;
	}
}

/*
 *  Size and Build Current options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  Either builds the current options requested or builds all current options.
 */
STATIC size_t sctp_current_opts_size(sctp_t * sp, sctp_opts_t * ops)
{
	size_t len = 0;
	const size_t hlen = sizeof(struct t_opthdr);
	const size_t olen = hlen + sizeof(t_scalar_t);
	if (!ops || ops->bcast) {
		len += olen;
	}
	if (!ops || ops->norte) {
		len += olen;
	}
	if (!ops || ops->opts) {
		len += olen;
	}
	if (!ops || ops->reuse) {
		len += olen;
	}
	if (!ops || ops->tos) {
		len += olen;
	}
	if (!ops || ops->ttl) {
		len += olen;
	}
	if (!ops || ops->nd) {
		len += olen;
	}
	if (!ops || ops->cork) {
		len += olen;
	}
	if (!ops || ops->ppi) {
		len += olen;
	}
	if (!ops || ops->sid) {
		len += olen;
	}
	if (!ops || ops->ropt) {
		len += olen;
	}
	if (!ops || ops->cklife) {
		len += olen;
	}
	if (!ops || ops->sack) {
		len += olen;
	}
	if (!ops || ops->path) {
		len += olen;
	}
	if (!ops || ops->assoc) {
		len += olen;
	}
	if (!ops || ops->init) {
		len += olen;
	}
	if (!ops || ops->hbitvl) {
		len += olen;
	}
	if (!ops || ops->rtoinit) {
		len += olen;
	}
	if (!ops || ops->rtomin) {
		len += olen;
	}
	if (!ops || ops->rtomax) {
		len += olen;
	}
	if (!ops || ops->ostr) {
		len += olen;
	}
	if (!ops || ops->istr) {
		len += olen;
	}
	if (!ops || ops->ckinc) {
		len += olen;
	}
	if (!ops || ops->titvl) {
		len += olen;
	}
	if (!ops || ops->hmac) {
		len += olen;
	}
	if (!ops || ops->mseg) {
		len += olen;
	}
	if (!ops || ops->debug) {
		len += olen;
	}
	if (!ops || ops->hb) {
		size_t n = ops ? (ops->hb->len - hlen) / sizeof(t_sctp_hb_t) : sp->danum;
		if (!n)
			n = sp->danum;
		if (n)
			len += hlen + n * sizeof(t_sctp_hb_t);
	}
	if (!ops || ops->rto) {
		size_t n = ops ? (ops->rto->len - hlen) / sizeof(t_sctp_rto_t) : sp->danum;
		if (!n)
			n = sp->danum;
		if (n)
			len += hlen + n * sizeof(t_sctp_rto_t);
	}
	if (!ops || ops->status) {
		size_t n =
		    ops ? (ops->status->len - hlen -
			   sizeof(t_sctp_status_t)) / sizeof(t_sctp_dest_status_t) : sp->danum;
		if (!n)
			n = sp->danum;
		if (n)
			len += hlen + sizeof(t_sctp_status_t) + n * sizeof(t_sctp_dest_status_t);
	}
	return (len);
}
STATIC void sctp_build_current_opts(sctp_t * sp, sctp_opts_t * ops, unsigned char **p)
{
	struct t_opthdr *oh;
	const size_t hlen = sizeof(*oh);
	const size_t olen = hlen + sizeof(t_scalar_t);

	if (!ops || ops->bcast) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_BROADCAST;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->ip_broadcast ? T_YES : T_NO;
	}
	if (!ops || ops->norte) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_DONTROUTE;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->ip_dontroute ? T_YES : T_NO;
	}
	if (!ops || ops->opts) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_OPTIONS;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = 0;
	}
	if (!ops || ops->reuse) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_REUSEADDR;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->i_flags & TF_IP_REUSEADDR ? T_YES : T_NO;
	}
	if (!ops || ops->tos) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_TOS;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->ip_tos;
	}
	if (!ops || ops->ttl) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_TTL;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->ip_ttl;
	}
	if (!ops || ops->nd) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_NODELAY;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->options & SCTP_OPTION_NAGLE ? T_NO : T_YES;
	}
	if (!ops || ops->cork) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_CORK;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->options & SCTP_OPTION_CORK ? T_YES : T_NO;
	}
	if (!ops || ops->ppi) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_PPI;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->ppi;
	}
	if (!ops || ops->sid) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_SID;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->sid;
	}
	/* note ssn and tsn are per-packet */

	if (!ops || ops->ropt) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RECVOPT;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->i_flags & TF_SCTP_RECVOPT ? T_YES : T_NO;
	}
	if (!ops || ops->cklife) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_COOKIE_LIFE;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = (sp->ck_life * 1000 + HZ - 1) / HZ;
	}
	if (!ops || ops->sack) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_SACK_DELAY;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = (sp->max_sack * 1000 + HZ - 1) / HZ;
	}
	if (!ops || ops->path) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_PATH_MAX_RETRANS;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->rtx_path;
	}
	if (!ops || ops->assoc) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_ASSOC_MAX_RETRANS;
		oh->status = T_SUCCESS;
		*((t_uscalar_t *) * p)++ = sp->max_retrans;
	}
	if (!ops || ops->init) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_MAX_INIT_RETRIES;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->max_inits;
	}
	if (!ops || ops->hbitvl) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_HEARTBEAT_ITVL;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = (sp->hb_itvl * 1000 + HZ - 1) / HZ;
	}
	if (!ops || ops->rtoinit) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RTO_INITIAL;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = (sp->rto_ini * 1000 + HZ - 1) / HZ;
	}
	if (!ops || ops->rtomin) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RTO_MIN;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = (sp->rto_min * 1000 + HZ - 1) / HZ;
	}
	if (!ops || ops->rtomax) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RTO_MAX;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = (sp->rto_max * 1000 + HZ - 1) / HZ;
	}
	if (!ops || ops->ostr) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_OSTREAMS;
		oh->status = T_SUCCESS;
		if ((1 << sp->i_state) & (TSF_DATA_XFER | TSF_WIND_ORDREL | TSF_WREQ_ORDREL))
			*((t_scalar_t *) * p)++ = sp->n_ostr;
		else
			*((t_scalar_t *) * p)++ = sp->req_ostr;
	}
	if (!ops || ops->istr) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_ISTREAMS;
		oh->status = T_SUCCESS;
		if ((1 << sp->i_state) & (TSF_DATA_XFER | TSF_WIND_ORDREL | TSF_WREQ_ORDREL))
			*((t_scalar_t *) * p)++ = sp->n_istr;
		else
			*((t_scalar_t *) * p)++ = sp->max_istr;
	}
	if (!ops || ops->ckinc) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_COOKIE_INC;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = (sp->ck_inc * 1000 + HZ - 1) / HZ;
	}
	if (!ops || ops->titvl) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_THROTTLE_ITVL;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = (sp->throttle * 1000 + HZ - 1) / HZ;
	}
	if (!ops || ops->hmac) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_MAC_TYPE;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->hmac;
	}
	if (!ops || ops->mseg) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_MAXSEG;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->pmtu;
	}
	if (!ops || ops->debug) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_DEBUG;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = sp->options;
	}
	if (!ops || ops->hb) {
		size_t n = ops ? (ops->hb->len - hlen) / sizeof(t_sctp_hb_t) : sp->danum;
		if (!n)
			n = sp->danum;
		if (n) {
			sctp_daddr_t *sd;

			oh = ((struct t_opthdr *) *p)++;
			oh->len = sizeof(*oh) + n * sizeof(t_sctp_hb_t);
			oh->level = T_INET_SCTP;
			oh->name = T_SCTP_HB;
			oh->status = T_SUCCESS;

			for (sd = sp->daddr; n && sd; n--, sd = sd->next) {
				t_sctp_hb_t *hb = ((t_sctp_hb_t *) * p)++;
				hb->hb_dest = sd->daddr;
				hb->hb_onoff = sd->hb_onoff;
				hb->hb_itvl = (sd->hb_itvl * 1000 + HZ - 1) / HZ;
			}
		}
	}
	if (!ops || ops->rto) {
		size_t n = ops ? (ops->rto->len - hlen) / sizeof(t_sctp_rto_t) : sp->danum;
		if (!n)
			n = sp->danum;
		if (n) {
			sctp_daddr_t *sd;

			oh = ((struct t_opthdr *) *p)++;
			oh->len = sizeof(*oh) + n * sizeof(t_sctp_rto_t);
			oh->level = T_INET_SCTP;
			oh->name = T_SCTP_RTO;
			oh->status = T_SUCCESS;

			for (sd = sp->daddr; n && sd; n--, sd = sd->next) {
				t_sctp_rto_t *rto = ((t_sctp_rto_t *) * p)++;
				rto->rto_dest = sd->daddr;
				rto->rto_initial = (sp->rto_ini * 1000 + HZ - 1) / HZ;
				rto->rto_min = (sd->rto_min * 1000 + HZ - 1) / HZ;
				rto->rto_max = (sd->rto_max * 1000 + HZ - 1) / HZ;
				rto->max_retrans = sd->max_retrans;
			}
		}
	}
	if (!ops || ops->status) {
		size_t n =
		    ops ? (ops->status->len - hlen -
			   sizeof(t_sctp_status_t)) / sizeof(t_sctp_dest_status_t) : sp->danum;
		if (!n)
			n = sp->danum;
		if (n) {
			sctp_daddr_t *sd;
			t_sctp_status_t *curr;

			oh = ((struct t_opthdr *) *p)++;
			oh->len = sizeof(*oh) + sizeof(t_sctp_status_t) + n * sizeof(t_sctp_dest_status_t);
			oh->level = T_INET_SCTP;
			oh->name = T_SCTP_STATUS;
			oh->status = T_SUCCESS;

			curr = ((t_sctp_status_t *) * p)++;
			curr->curr_rwnd = sp->p_rwnd - sp->in_flight;
			curr->curr_rbuf = sp->a_rwnd;
			curr->curr_nrep = n;

			for (sd = sp->daddr; n && sd; n--, sd = sd->next) {
				t_sctp_dest_status_t *dest = ((t_sctp_dest_status_t *) * p)++;
				dest->dest_addr = sd->daddr;
				dest->dest_cwnd = sd->cwnd;
				dest->dest_unack = sd->in_flight;
				dest->dest_srtt = (sd->srtt * 1000 + HZ - 1) / HZ;
				dest->dest_rvar = sd->rttvar;
				dest->dest_rto = (sd->rto * 1000 + HZ - 1) / HZ;
				dest->dest_sst = sd->ssthresh;
			}
		}
	}
}

/*
 *  Size and Build Checked or Negotiated options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  Builds the checked or negotiated options.
 */
STATIC size_t sctp_set_opts_size(sctp_t * sp, sctp_opts_t * ops)
{
	size_t len = 0;
	const size_t hlen = sizeof(struct t_opthdr);
	const size_t olen = hlen + sizeof(t_scalar_t);
	if (ops->bcast) {
		len += olen;
	}
	if (ops->norte) {
		len += olen;
	}
	if (ops->opts) {
		len += olen;
	}
	if (ops->reuse) {
		len += olen;
	}
	if (ops->tos) {
		len += olen;
	}
	if (ops->ttl) {
		len += olen;
	}
	if (ops->nd) {
		len += olen;
	}
	if (ops->cork) {
		len += olen;
	}
	if (ops->ppi) {
		len += olen;
	}
	if (ops->sid) {
		len += olen;
	}
	if (ops->ssn) {
		len += olen;
	}
	if (ops->tsn) {
		len += olen;
	}
	if (ops->ropt) {
		len += olen;
	}
	if (ops->cklife) {
		len += olen;
	}
	if (ops->sack) {
		len += olen;
	}
	if (ops->path) {
		len += olen;
	}
	if (ops->assoc) {
		len += olen;
	}
	if (ops->init) {
		len += olen;
	}
	if (ops->hbitvl) {
		len += olen;
	}
	if (ops->rtoinit) {
		len += olen;
	}
	if (ops->rtomin) {
		len += olen;
	}
	if (ops->rtomax) {
		len += olen;
	}
	if (ops->ostr) {
		len += olen;
	}
	if (ops->istr) {
		len += olen;
	}
	if (ops->ckinc) {
		len += olen;
	}
	if (ops->titvl) {
		len += olen;
	}
	if (ops->hmac) {
		len += olen;
	}
	if (ops->mseg) {
		len += olen;
	}
	if (ops->debug) {
		len += olen;
	}
	if (ops->hb) {
		len += ops->hb->len;
	}
	if (ops->rto) {
		len += ops->rto->len;
	}
	if (ops->status) {
		len += ops->status->len;
	}
	return (len);
}
STATIC void sctp_build_set_opts(sctp_t * sp, sctp_opts_t * ops, unsigned char **p)
{
	struct t_opthdr *oh;
	const size_t hlen = sizeof(*oh);
	const size_t olen = hlen + sizeof(t_scalar_t);

	if (ops->bcast) {
		oh = ((struct t_opthdr *) *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)++ = *((t_scalar_t *) (ops->bcast + 1));
	}
	if (ops->norte) {
		oh = ((struct t_opthdr *) *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)++ = *((t_scalar_t *) (ops->norte + 1));
	}
	if (ops->opts) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_IP;
		oh->name = T_IP_OPTIONS;
		oh->status = ops->flags & TF_IP_OPTIONS ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->opts + 1));
	}
	if (ops->reuse) {
		oh = ((struct t_opthdr *) *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)++ = *((t_scalar_t *) (ops->reuse + 1));
	}
	if (ops->tos) {
		oh = ((struct t_opthdr *) *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)++ = *((t_scalar_t *) (ops->tos + 1));
	}
	if (ops->ttl) {
		oh = ((struct t_opthdr *) *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)++ = *((t_scalar_t *) (ops->ttl + 1));
	}
	if (ops->nd) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_NODELAY;
		oh->status = ops->flags & TF_SCTP_NODELAY ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->nd + 1));
	}
	if (ops->cork) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_CORK;
		oh->status = ops->flags & TF_SCTP_CORK ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->cork + 1));
	}
	if (ops->ppi) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_PPI;
		oh->status = ops->flags & TF_SCTP_PPI ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->ppi + 1));
	}
	if (ops->sid) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_SID;
		oh->status = ops->flags & TF_SCTP_SID ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->sid + 1));
	}
	if (ops->ssn) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_SSN;
		oh->status = ops->flags & TF_SCTP_SSN ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->ssn + 1));
	}
	if (ops->tsn) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_TSN;
		oh->status = ops->flags & TF_SCTP_TSN ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->tsn + 1));
	}
	if (ops->ropt) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RECVOPT;
		oh->status = ops->flags & TF_SCTP_RECVOPT ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->ropt + 1));
	}
	if (ops->cklife) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_COOKIE_LIFE;
		oh->status = ops->flags & TF_SCTP_COOKIE_LIFE ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->cklife + 1));
	}
	if (ops->sack) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_SACK_DELAY;
		oh->status = ops->flags & TF_SCTP_SACK_DELAY ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->sack + 1));
	}
	if (ops->path) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_PATH_MAX_RETRANS;
		oh->status = ops->flags & TF_SCTP_PATH_MAX_RETRANS ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->path + 1));
	}
	if (ops->assoc) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_ASSOC_MAX_RETRANS;
		oh->status = ops->flags & TF_SCTP_ASSOC_MAX_RETRANS ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->assoc + 1));
	}
	if (ops->init) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_MAX_INIT_RETRIES;
		oh->status = ops->flags & TF_SCTP_MAX_INIT_RETRIES ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->init + 1));
	}
	if (ops->hbitvl) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_HEARTBEAT_ITVL;
		oh->status = ops->flags & TF_SCTP_HEARTBEAT_ITVL ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->hbitvl + 1));
	}
	if (ops->rtoinit) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RTO_INITIAL;
		oh->status = ops->flags & TF_SCTP_RTO_INITIAL ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->rtoinit + 1));
	}
	if (ops->rtomin) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RTO_MIN;
		oh->status = ops->flags & TF_SCTP_RTO_MIN ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->rtomin + 1));
	}
	if (ops->rtomax) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RTO_MAX;
		oh->status = ops->flags & TF_SCTP_RTO_MAX ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->rtomax + 1));
	}
	if (ops->ostr) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_OSTREAMS;
		oh->status = ops->flags & TF_SCTP_OSTREAMS ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->ostr + 1));
	}
	if (ops->istr) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_ISTREAMS;
		oh->status = ops->flags & TF_SCTP_ISTREAMS ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->istr + 1));
	}
	if (ops->ckinc) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_COOKIE_INC;
		oh->status = ops->flags & TF_SCTP_COOKIE_INC ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->ckinc + 1));
	}
	if (ops->titvl) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_THROTTLE_ITVL;
		oh->status = ops->flags & TF_SCTP_THROTTLE_ITVL ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->titvl + 1));
	}
	if (ops->hmac) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_MAC_TYPE;
		oh->status = ops->flags & TF_SCTP_MAC_TYPE ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->hmac + 1));
	}
	if (ops->mseg) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_MAXSEG;
		oh->status = ops->flags & TF_SCTP_MAXSEG ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->mseg + 1));
	}
	if (ops->debug) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_DEBUG;
		oh->status = ops->flags & TF_SCTP_DEBUG ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = *((t_scalar_t *) (ops->debug + 1));
	}
	if (ops->hb) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = ops->hb->len;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_HB;
		oh->status = ops->flags & TF_SCTP_HB ? T_SUCCESS : T_FAILURE;
		bcopy(ops->hb + 1, *p, ops->hb->len - sizeof(*oh));
		*p += ops->hb->len - sizeof(*oh);
	}
	if (ops->rto) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = ops->rto->len;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_RTO;
		oh->status = ops->flags & TF_SCTP_RTO ? T_SUCCESS : T_FAILURE;
		bcopy(ops->rto + 1, *p, ops->rto->len - sizeof(*oh));
		*p += ops->rto->len - sizeof(*oh);
	}
	if (ops->status) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = ops->status->len;
		oh->level = T_INET_SCTP;
		oh->name = T_SCTP_STATUS;
		oh->status = ops->flags & TF_SCTP_STATUS ? T_SUCCESS : T_FAILURE;
		bcopy(ops->status + 1, *p, ops->status->len - sizeof(*oh));
		*p += ops->status->len - sizeof(*oh);
	}
}

/*
 *  Size and Build options.
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC size_t sctp_opts_size(ulong flags, sctp_t * sp, sctp_opts_t * ops)
{
	switch (flags) {
	case T_CHECK:
	case T_NEGOTIATE:
		if (ops)
			return sctp_set_opts_size(sp, ops);
	case T_DEFAULT:
		return sctp_default_opts_size(sp, ops);
	case T_CURRENT:
		return sctp_current_opts_size(sp, ops);
	}
	return (0);
}
STATIC void sctp_build_opts(ulong flags, sctp_t * sp, sctp_opts_t * ops, unsigned char **p)
{
	switch (flags) {
	case T_CHECK:
	case T_NEGOTIATE:
		if (ops)
			return sctp_build_set_opts(sp, ops, p);
	case T_DEFAULT:
		return sctp_build_default_opts(sp, ops, p);
	case T_CURRENT:
		return sctp_build_current_opts(sp, ops, p);
	}
	return;
}

/*
 *  Parse options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC int sctp_parse_opts(sctp_opts_t * ops, unsigned char *opt_ptr, size_t opt_len)
{
	struct t_opthdr *oh = (struct t_opthdr *) opt_ptr;
	unsigned char *opt_end = opt_ptr + opt_len;
	for (; opt_ptr + sizeof(*oh) <= opt_end && oh->len >= sizeof(*oh)
	     && opt_ptr + oh->len <= opt_end; opt_ptr += oh->len, oh = (struct t_opthdr *) opt_ptr) {
		switch (oh->level) {
		case T_INET_IP:
			switch (oh->name) {
			case T_IP_BROADCAST:
				ops->bcast = oh;
				continue;
			case T_IP_DONTROUTE:
				ops->norte = oh;
				continue;
			case T_IP_OPTIONS:
				ops->opts = oh;
				continue;
			case T_IP_REUSEADDR:
				ops->reuse = oh;
				continue;
			case T_IP_TOS:
				ops->tos = oh;
				continue;
			case T_IP_TTL:
				ops->ttl = oh;
				continue;
			}
			break;
		case T_INET_SCTP:
			switch (oh->name) {
			case T_SCTP_NODELAY:
				ops->nd = oh;
				continue;
			case T_SCTP_CORK:
				ops->cork = oh;
				continue;
			case T_SCTP_PPI:
				ops->ppi = oh;
				continue;
			case T_SCTP_SID:
				ops->sid = oh;
				continue;
			case T_SCTP_SSN:
				ops->ssn = oh;
				continue;
			case T_SCTP_TSN:
				ops->tsn = oh;
				continue;
			case T_SCTP_RECVOPT:
				ops->ropt = oh;
				continue;
			case T_SCTP_COOKIE_LIFE:
				ops->cklife = oh;
				continue;
			case T_SCTP_SACK_DELAY:
				ops->sack = oh;
				continue;
			case T_SCTP_PATH_MAX_RETRANS:
				ops->path = oh;
				continue;
			case T_SCTP_ASSOC_MAX_RETRANS:
				ops->assoc = oh;
				continue;
			case T_SCTP_MAX_INIT_RETRIES:
				ops->init = oh;
				continue;
			case T_SCTP_HEARTBEAT_ITVL:
				ops->hbitvl = oh;
				continue;
			case T_SCTP_RTO_INITIAL:
				ops->rtoinit = oh;
				continue;
			case T_SCTP_RTO_MIN:
				ops->rtomin = oh;
				continue;
			case T_SCTP_RTO_MAX:
				ops->rtomax = oh;
				continue;
			case T_SCTP_OSTREAMS:
				ops->ostr = oh;
				continue;
			case T_SCTP_ISTREAMS:
				ops->istr = oh;
				continue;
			case T_SCTP_COOKIE_INC:
				ops->ckinc = oh;
				continue;
			case T_SCTP_THROTTLE_ITVL:
				ops->titvl = oh;
				continue;
			case T_SCTP_MAC_TYPE:
				ops->hmac = oh;
				continue;
			case T_SCTP_MAXSEG:
				ops->mseg = oh;
				continue;
			case T_SCTP_DEBUG:
				ops->debug = oh;
				continue;
			case T_SCTP_HB:
				ops->hb = oh;
				continue;
			case T_SCTP_RTO:
				ops->rto = oh;
				continue;
			case T_SCTP_STATUS:
				ops->status = oh;
				continue;
			}
			break;
		}
		return (TBADOPT);
	}
	if (opt_ptr != opt_end)
		return (TBADOPT);
	return (0);
}

/*
 *  Negotiate options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC void sctp_negotiate_opts(sctp_t * sp, sctp_opts_t * ops)
{
	const size_t hlen = sizeof(struct t_opthdr);
	const size_t olen = hlen + sizeof(t_scalar_t);

	if (!ops)
		return;

	if (ops->bcast) {
		if (ops->bcast->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->bcast + 1);
			switch (*val) {
			case T_YES:
				sp->ip_broadcast = 1;
				break;
			case T_NO:
				sp->ip_broadcast = 0;
				break;
			}
			*val = sp->ip_broadcast;
			ops->flags |= TF_IP_BROADCAST;
		}
	}
	if (ops->norte) {
		if (ops->norte->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->norte + 1);
			switch (*val) {
			case T_YES:
				sp->ip_dontroute = 1;
				break;
			case T_NO:
				sp->ip_dontroute = 0;
				break;
			}
			*val = sp->ip_dontroute;
			ops->flags |= TF_IP_DONTROUTE;
		}
	}
	if (ops->opts) {
		/* not supported yet */
	}
	if (ops->reuse) {
		/* not supported yet */
	}
	if (ops->tos) {
		if (ops->tos->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->tos + 1);
			sp->ip_tos = *val & 0xff;
			*val = sp->ip_tos;
			ops->flags |= TF_IP_TOS;
		}
	}
	if (ops->ttl) {
		if (ops->ttl->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->ttl + 1);
			sp->ip_ttl = *val & 0xff;
			*val = sp->ip_ttl;
			ops->flags |= TF_IP_TTL;
		}
	}
	if (ops->nd) {
		if (ops->nd->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->nd + 1);
			switch (*val) {
			case T_YES:
				sp->options &= ~SCTP_OPTION_NAGLE;
				break;
			case T_NO:
				sp->options |= SCTP_OPTION_NAGLE;
				break;
				break;
			}
			*val = (sp->options & SCTP_OPTION_NAGLE) ? T_NO : T_YES;
			ops->flags |= TF_SCTP_NODELAY;
		}
	}
	if (ops->cork) {
		if (ops->cork->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->cork + 1);
			switch (*val) {
			case T_YES:
				sp->options |= SCTP_OPTION_CORK;
				break;
			case T_NO:
				sp->options &= ~SCTP_OPTION_CORK;
				break;
			}
			*val = (sp->options & SCTP_OPTION_CORK) ? T_YES : T_NO;
			ops->flags |= TF_SCTP_CORK;
		}
	}
	if (ops->ppi) {
		if (ops->ppi->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->ppi + 1);
			sp->ppi = *val;
			*val = sp->ppi;
			ops->flags |= TF_SCTP_PPI;
		}
	}
	if (ops->sid) {
		if (ops->sid->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->sid + 1);
			sp->sid = *val;
			*val = sp->sid;
			ops->flags |= TF_SCTP_SID;
		}
	}
	if (ops->ssn) {
		/* not writeable */
	}
	if (ops->tsn) {
		/* not writeable */
	}
	if (ops->ropt) {
		if (ops->ropt->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->ropt + 1);
			switch (*val) {
			case T_YES:
				sp->i_flags |= TF_SCTP_RECVOPT;
				break;
			case T_NO:
				sp->i_flags &= ~TF_SCTP_RECVOPT;
				break;
			}
			*val = (sp->i_flags & TF_SCTP_RECVOPT) ? T_YES : T_NO;
			ops->flags |= TF_SCTP_RECVOPT;
		}
	}
	if (ops->cklife) {
		if (ops->cklife->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->cklife + 1);
			if (*val < 10)
				*val = 10;
			sp->ck_life = (*val * HZ + 999) / 1000;
			*val = (sp->ck_life * 1000 + HZ - 1) / HZ;
			ops->flags |= TF_SCTP_COOKIE_LIFE;
		}
	}
	if (ops->sack) {
		if (ops->sack->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->sack + 1);
			sp->max_sack = (*val * HZ + 999) / 1000;
			*val = (sp->max_sack * 1000 + HZ - 1) / HZ;
			ops->flags |= TF_SCTP_SACK_DELAY;
		}
	}
	if (ops->path) {
		if (ops->path->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->path + 1);
			sp->rtx_path = *val;
			*val = sp->rtx_path;
			ops->flags |= TF_SCTP_PATH_MAX_RETRANS;
		}
	}
	if (ops->assoc) {
		if (ops->assoc->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->assoc + 1);
			sp->max_retrans = *val;
			*val = sp->max_retrans;
			ops->flags |= TF_SCTP_ASSOC_MAX_RETRANS;
		}
	}
	if (ops->init) {
		if (ops->init->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->init + 1);
			sp->max_inits = *val;
			*val = sp->max_inits;
			ops->flags |= TF_SCTP_MAX_INIT_RETRIES;
		}
	}
	if (ops->hbitvl) {
		if (ops->hbitvl->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->hbitvl + 1);
			sp->hb_itvl = (*val * HZ + 999) / 1000;
			*val = (sp->hb_itvl * 1000 + HZ - 1) / HZ;
			ops->flags |= TF_SCTP_HEARTBEAT_ITVL;
		}
	}
	if (ops->rtoinit) {
		if (ops->rtoinit->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->rtoinit + 1);
			sp->rto_ini = (*val * HZ + 999) / 1000;
			*val = (sp->rto_ini * 1000 + HZ - 1) / HZ;
			ops->flags |= TF_SCTP_RTO_INITIAL;
		}
	}
	if (ops->rtomin) {
		if (ops->rtomin->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->rtomin + 1);
			sp->rto_min = (*val * HZ + 999) / 1000;
			*val = (sp->rto_min * 1000 + HZ - 1) / HZ;
			ops->flags |= TF_SCTP_RTO_MIN;
		}
	}
	if (ops->rtomax) {
		if (ops->rtomax->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->rtomax + 1);
			sp->rto_max = (*val * HZ + 999) / 1000;
			*val = (sp->rto_max * 1000 + HZ - 1) / HZ;
			ops->flags |= TF_SCTP_RTO_MAX;
		}
	}
	if (ops->ostr) {
		if (ops->ostr->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->ostr + 1);
			sp->req_ostr = *val;
			*val = sp->req_ostr;
			ops->flags |= TF_SCTP_OSTREAMS;
		}
	}
	if (ops->istr) {
		if (ops->istr->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->istr + 1);
			sp->max_istr = *val;
			*val = sp->max_istr;
			ops->flags |= TF_SCTP_ISTREAMS;
		}
	}
	if (ops->ckinc) {
		if (ops->ckinc->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->ckinc + 1);
			sp->ck_inc = (*val * HZ + 999) / 1000;
			*val = (sp->ck_inc * 1000 + HZ - 1) / HZ;
			ops->flags |= TF_SCTP_COOKIE_INC;
		}
	}
	if (ops->titvl) {
		if (ops->titvl->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->titvl + 1);
			sp->throttle = (*val * HZ + 999) / 1000;
			*val = (sp->throttle * 1000 + HZ - 1) / HZ;
			ops->flags |= TF_SCTP_THROTTLE_ITVL;
		}
	}
	if (ops->hmac) {
		if (ops->hmac->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->hmac + 1);
			sp->hmac = *val;
			*val = sp->hmac;
			ops->flags |= TF_SCTP_MAC_TYPE;
		}
	}
	if (ops->mseg) {
		/* not writeable */
	}
	if (ops->debug) {
		if (ops->debug->len >= olen) {
			t_scalar_t *val = (t_scalar_t *) (ops->debug + 1);
			sp->options = *val;
			*val = sp->options;
			ops->flags |= TF_SCTP_DEBUG;
		}
	}
	if (ops->hb) {
		/* not support yet */
	}
	if (ops->rto) {
		/* not support yet */
	}
	if (ops->status) {
		/* not writeable */
	}
	return;
}

/*
 *  Check options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC void sctp_check_opts(sctp_t * sp, sctp_opts_t * ops)
{
	if (!ops)
		return;
	ops->flags = TF_SCTP_ALLOPS;
	/* 
	 *  FIXME: actually check some options.
	 */
	fixme(("Actually check some options.\n"));
	return;
}

/*
 *  =========================================================================
 *
 *  SCTP T-Provider --> T-User Primitives (Indication, Confirmation and Ack)
 *
 *  =========================================================================
 *
 *  T_CONN_IND	    11 - connection indication
 *  -----------------------------------------------------------------
 *  We get the connection indication information from the cookie received in the COOKIE ECHO
 *  which invokes the indication.  (We queue the COOKIE ECHO chunks themselves as
 *  indications.)
 */
STATIC int t_conn_ind(sctp_t * sp, mblk_t * cp)
{
	mblk_t *mp;
	struct T_conn_ind *p;
	struct t_opthdr *oh;
	struct sctp_cookie_echo *m = (struct sctp_cookie_echo *) cp->b_rptr;
	struct sctp_cookie *ck = (struct sctp_cookie *) m->cookie;
	size_t danum = ck->danum + 1;
	uint32_t *daptr = (uint32_t *) (((caddr_t) (ck + 1) + ck->opt_len));
	size_t src_len = danum ? sizeof(uint16_t) + danum * sizeof(uint32_t) : 0;
	size_t str_len = sizeof(struct t_opthdr) + sizeof(t_scalar_t);
	size_t opt_len = 2 * str_len;
	ensure(((1 << sp->i_state) & (TSF_IDLE | TSF_WRES_CIND)), return (-EFAULT));
	if (bufq_length(&sp->conq) < sp->conind) {
		if (canputnext(sp->rq)) {
			if ((mp = allocb(sizeof(*p) + src_len + opt_len, BPRI_MED))) {
				mp->b_datap->db_type = M_PROTO;
				p = ((struct T_conn_ind *) 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;

				/* place address information from cookie */
				if (danum)
					*((uint16_t *) mp->b_wptr)++ = ck->dport;
				if (danum--)
					*((uint32_t *) mp->b_wptr)++ = ck->daddr;
				while (danum--)
					*((uint32_t *) mp->b_wptr)++ = *daptr++;

				/* indicate options */
				oh = ((struct t_opthdr *) mp->b_wptr)++;
				oh->len = str_len;
				oh->level = T_INET_SCTP;
				oh->name = T_SCTP_ISTREAMS;
				oh->status = T_SUCCESS;
				*((t_scalar_t *) mp->b_wptr)++ = ck->n_istr;

				/* indicate options */
				oh = ((struct t_opthdr *) mp->b_wptr)++;
				oh->len = str_len;
				oh->level = T_INET_SCTP;
				oh->name = T_SCTP_OSTREAMS;
				oh->status = T_SUCCESS;
				*((t_scalar_t *) mp->b_wptr)++ = ck->n_ostr;

				bufq_queue(&sp->conq, cp);

				sp->i_state = TS_WRES_CIND;
				putnext(sp->rq, mp);
				return (0);
			}
			seldom();
			return (-ENOBUFS);
		}
		seldom();
		return (-EBUSY);
	}
	seldom();
	return (-ERESTART);
}

/*
 *  T_CONN_CON	    12 - connection confirmation
 *  -----------------------------------------------------------------
 *  The only options with end-to-end significance that are negotiated are the number of
 *  inbound and outbound streams.
 */
STATIC int t_conn_con(sctp_t * sp)
{
	mblk_t *mp;
	struct T_conn_con *p;
	struct t_opthdr *oh;
	struct sctp_daddr *sd = sp->daddr;
	size_t res_len = sp->danum ? sizeof(uint16_t) + sp->danum * sizeof(sd->daddr) : 0;
	size_t str_len = sizeof(*oh) + sizeof(t_scalar_t);
	size_t opt_len = 2 * str_len;
	ensure((sp->i_state == TS_WCON_CREQ), return (-EFAULT));
	if (canputnext(sp->rq)) {
		if ((mp = allocb(sizeof(*p) + res_len + opt_len, BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			mp->b_band = 1;	/* expedite */
			p = ((struct T_conn_con *) 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;

			/* place destination (responding) address */
			if (sd)
				*((uint16_t *) mp->b_wptr)++ = sp->dport;
			for (; sd; sd = sd->next)
				*((uint32_t *) mp->b_wptr)++ = sd->daddr;

			/* indicate options */
			oh = ((struct t_opthdr *) mp->b_wptr)++;
			oh->len = str_len;
			oh->level = T_INET_SCTP;
			oh->name = T_SCTP_ISTREAMS;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) mp->b_wptr)++ = sp->n_istr;

			/* indicate options */
			oh = ((struct t_opthdr *) mp->b_wptr)++;
			oh->len = str_len;
			oh->level = T_INET_SCTP;
			oh->name = T_SCTP_OSTREAMS;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) mp->b_wptr)++ = sp->n_ostr;

			sp->i_state = TS_DATA_XFER;
			putnext(sp->rq, mp);
			return (0);
		}
		seldom();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}

/*
 *  T_DISCON_IND    13 - disconnect indication
 *  -----------------------------------------------------------------
 *  We use the address of the mblk that contains the COOKIE-ECHO chunk as a SEQ_number for
 *  connect indications that are rejected with a disconnect indication as well.  We can use
 *  this to directly address the mblk in the connection indication bufq.
 *
 *  If the caller provides disconnect data, the caller needs to set the current ord, ppi,
 *  sid, and ssn fields so that the user can examine them with T_OPTMGMT_REQ T_CURRENT if it
 *  has need to know them.
 */
STATIC int t_discon_ind(sctp_t * sp, long reason, mblk_t * seq)
{
	mblk_t *mp;
	struct T_discon_ind *p;
	ensure(((1 << sp->
		 i_state) & (TSF_WCON_CREQ | TSF_WRES_CIND | TSF_DATA_XFER | TSF_WIND_ORDREL | TSF_WREQ_ORDREL)),
	       return (-EFAULT));
	if (canputnext(sp->rq)) {
		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((struct T_discon_ind *) mp->b_wptr)++;
			p->PRIM_type = T_DISCON_IND;
			p->DISCON_reason = reason;
			p->SEQ_number = (ulong) seq;
			if (seq) {
				bufq_unlink(&sp->conq, seq);
				freemsg(seq);
			}
			if (!bufq_length(&sp->conq))
				sp->i_state = TS_IDLE;
			else
				sp->i_state = TS_WRES_CIND;
			putnext(sp->rq, mp);
			return (0);
		}
		seldom();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}

/*
 *  T_DATA_IND	    14 - data indication
 *  -----------------------------------------------------------------
 *  This indication is only useful for delivering data indications for the default stream.
 *  The caller should check that ppi and sid match the default before using this indication.
 *  Otherwise the caller should use the T_OPTDATA_IND.
 */
STATIC int t_data_ind(sctp_t * sp, ulong more, mblk_t * dp)
{
	mblk_t *mp;
	struct T_data_ind *p;
	ensure(((1 << sp->i_state) & (TSF_DATA_XFER | TSF_WIND_ORDREL)), return (-EFAULT));
	if (canputnext(sp->rq)) {
		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((struct T_data_ind *) mp->b_wptr)++;
			p->PRIM_type = T_DATA_IND;
			p->MORE_flag = more;
			mp->b_cont = dp;
			putnext(sp->rq, mp);
			return (0);
		}
		seldom();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}

/*
 *  T_EXDATA_IND    15 - expedited data indication
 *  -----------------------------------------------------------------
 *  This indication is only useful for delivering data indications for the default stream.
 *  The caller should check that ppi and ssn match the default before using this indication.
 *  Otherwise the caller should use the T_OPTDATA_IND.
 */
STATIC int t_exdata_ind(sctp_t * sp, ulong more, mblk_t * dp)
{
	mblk_t *mp;
	struct T_exdata_ind *p;
	ensure(((1 << sp->i_state) & (TSF_DATA_XFER | TSF_WIND_ORDREL)), return (-EFAULT));
	if (canputnext(sp->rq)) {
		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			mp->b_band = 1;	/* expedite */
			p = ((struct T_exdata_ind *) mp->b_wptr)++;
			p->PRIM_type = T_EXDATA_IND;
			p->MORE_flag = more;
			mp->b_cont = dp;
			putnext(sp->rq, mp);
			return (0);
		}
		seldom();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}

/*
 *  T_INFO_ACK	    16 - information acknowledgement
 *  -----------------------------------------------------------------
 *  Although there is no limit on CDATA and DDATA size, if these are too large then we will
 *  IP fragment the message.
 */
STATIC int t_info_ack(sctp_t * sp)
{
	mblk_t *mp;
	struct T_info_ack *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((struct T_info_ack *) mp->b_wptr)++;
		p->PRIM_type = T_INFO_ACK;
		p->TSDU_size = -1;	/* no limit on TSDU size */
		p->ETSDU_size = -1;	/* no limit on ETSDU size */
		p->CDATA_size = -1;	/* no limit on CDATA size */
		p->DDATA_size = -1;	/* no limit on DDATA size */
		p->ADDR_size = -1;	/* no limit on ADDR size */
		p->OPT_size = -1;	/* no limit on OPTIONS size */
		p->TIDU_size = -1;	/* no limit on TIDU size */
		p->SERV_type = T_COTS_ORD;	/* COTS with orderly release */
		p->CURRENT_state = sp->i_state;
		p->PROVIDER_flag = XPG4_1 & ~T_SNDZERO;
		putnext(sp->rq, mp);
		return (0);
	}
	seldom();
	return (-ENOBUFS);
}

/*
 *  T_BIND_ACK	    17 - bind acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_bind_ack(sctp_t * sp)
{
	mblk_t *mp;
	struct T_bind_ack *p;
	struct sctp_saddr *ss = sp->saddr;
	size_t add_len = sp->sanum ? sizeof(sp->sport) + sp->sanum * sizeof(ss->saddr) : 0;
	ensure((sp->i_state == TS_WACK_BREQ), return (-EFAULT));
	if ((mp = allocb(sizeof(*p) + add_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((struct T_bind_ack *) 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 = sp->conind;
		if (ss)
			*((typeof(sp->sport) *) mp->b_wptr)++ = sp->sport;
		for (; ss; ss = ss->next)
			*((typeof(ss->saddr) *) mp->b_wptr)++ = ss->saddr;
		sp->i_state = TS_IDLE;
		putnext(sp->rq, mp);
		return (0);
	}
	seldom();
	return (-ENOBUFS);
}

/*
 *  T_ERROR_ACK	    18 - error acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_error_ack(sctp_t * sp, ulong prim, long err)
{
	mblk_t *mp;
	struct T_error_ack *p;
	switch (err) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (err);
	case 0:
		never();
		return (err);
	}
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((struct T_error_ack *) mp->b_wptr)++;
		p->PRIM_type = T_ERROR_ACK;
		p->ERROR_prim = prim;
		p->TLI_error = err < 0 ? TSYSERR : err;
		p->UNIX_error = err < 0 ? -err : 0;
		switch (sp->i_state) {
#ifdef TS_WACK_OPTREQ
		case TS_WACK_OPTREQ:
#endif
		case TS_WACK_UREQ:
		case TS_WACK_CREQ:
			sp->i_state = TS_IDLE;
			break;
		case TS_WACK_BREQ:
			sp->i_state = TS_UNBND;
			break;
		case TS_WACK_CRES:
			sp->i_state = TS_WRES_CIND;
			break;
		case TS_WACK_DREQ6:
			sp->i_state = TS_WCON_CREQ;
			break;
		case TS_WACK_DREQ7:
			sp->i_state = TS_WRES_CIND;
			break;
		case TS_WACK_DREQ9:
			sp->i_state = TS_DATA_XFER;
			break;
		case TS_WACK_DREQ10:
			sp->i_state = TS_WIND_ORDREL;
			break;
		case TS_WACK_DREQ11:
			sp->i_state = TS_WREQ_ORDREL;
			break;
			/* 
			 *  Note: if we are not in a WACK state we simply do
			 *  not change state.  This occurs normally when we
			 *  send TOUTSTATE or TNOTSUPPORT or are responding to
			 *  a T_OPTMGMT_REQ in other then TS_IDLE state.
			 */
		}
		putnext(sp->rq, mp);
		return (0);
	}
	seldom();
	return (-ENOBUFS);
}

/*
 *  T_OK_ACK	    19 - success acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_ok_ack(sctp_t * sp, ulong prim, ulong seq, ulong tok)
{
	mblk_t *mp;
	struct T_ok_ack *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((struct T_ok_ack *) mp->b_wptr)++;
		p->PRIM_type = T_OK_ACK;
		p->CORRECT_prim = prim;
		switch (sp->i_state) {
		case TS_WACK_CREQ:
			sp->i_state = TS_WCON_CREQ;
			break;
		case TS_WACK_UREQ:
			sp->i_state = TS_UNBND;
			break;
		case TS_WACK_CRES:
		{
			queue_t *aq = (queue_t *) tok;
			sctp_t *ap = (sctp_t *) aq->q_ptr;
			if (ap) {
				ap->i_state = TS_DATA_XFER;
				sctp_cleanup_read(sp);	/* deliver to user what is possible */
				sctp_transmit_wakeup(ap);	/* reply to peer what is necessary */
			}
			if (seq) {
				bufq_unlink(&sp->conq, (mblk_t *) seq);
				freemsg((mblk_t *) seq);
			}
			if (aq != sp->rq) {
				if (bufq_length(&sp->conq))
					sp->i_state = TS_WRES_CIND;
				else
					sp->i_state = TS_IDLE;
			}
			break;
		}
		case TS_WACK_DREQ7:
			if (seq)
				bufq_unlink(&sp->conq, (mblk_t *) seq);
		case TS_WACK_DREQ6:
		case TS_WACK_DREQ9:
		case TS_WACK_DREQ10:
		case TS_WACK_DREQ11:
			if (bufq_length(&sp->conq))
				sp->i_state = TS_WRES_CIND;
			else
				sp->i_state = TS_IDLE;
			break;
			/* 
			 *  Note: if we are not in a WACK state we simply do
			 *  not change state.  This occurs normally when we
			 *  are responding to a T_OPTMGMT_REQ in other than
			 *  the TS_IDLE state.
			 */
		}
		putnext(sp->rq, mp);
		return (0);
	}
	seldom();
	return (-ENOBUFS);
}

/*
 *  T_OPTMGMT_ACK   22 - options management acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_optmgmt_ack(sctp_t * sp, ulong flags, sctp_opts_t * ops)
{
	mblk_t *mp;
	size_t opt_len = sctp_opts_size(flags, sp, ops);
	struct T_optmgmt_ack *p;
	if ((mp = allocb(sizeof(*p) + opt_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((struct T_optmgmt_ack *) 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;
		sctp_build_opts(flags, sp, ops, &mp->b_wptr);
#ifdef TS_WACK_OPTREQ
		if (sp->i_state == TS_WACK_OPTREQ)
			sp->i_state = TS_IDLE;
#endif
		putnext(sp->rq, mp);
		return (0);
	}
	seldom();
	return (-ENOBUFS);
}

/*
 *  T_ORDREL_IND    23 - orderly release indication
 *  -----------------------------------------------------------------
 */
STATIC int t_ordrel_ind(sctp_t * sp)
{
	mblk_t *mp;
	struct T_ordrel_ind *p;
	ensure(((1 << sp->i_state) & (TSF_DATA_XFER | TSF_WIND_ORDREL)), return (-EFAULT));
	if (canputnext(sp->rq)) {
		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((struct T_ordrel_ind *) mp->b_wptr)++;
			p->PRIM_type = T_ORDREL_IND;
			switch (sp->i_state) {
			case TS_DATA_XFER:
				sp->i_state = TS_WREQ_ORDREL;
				break;
			case TS_WIND_ORDREL:
				sp->i_state = TS_IDLE;
				break;
			}
			putnext(sp->rq, mp);
			return (0);
		}
		seldom();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}

/*
 *  T_OPTDATA_IND   26 - data with options indication
 *  -----------------------------------------------------------------
 */
STATIC int t_optdata_ind(sctp_t * sp, uint32_t ppi, uint16_t sid, uint16_t ssn, uint32_t tsn, uint ord, uint more,
			 mblk_t * dp)
{
	mblk_t *mp;
	struct t_opthdr *oh;
	struct T_optdata_ind *p;
	size_t str_len = sizeof(*oh) + sizeof(t_scalar_t);
	size_t opt_len = 0;
	ensure(((1 << sp->i_state) & (TSF_DATA_XFER | TSF_WREQ_ORDREL)), return (-EFAULT));
	if (canputnext(sp->rq)) {
		if (sp->i_flags & TF_SCTP_RECVOPT)
			opt_len = 4 * str_len;
		if ((mp = allocb(sizeof(*p) + opt_len, BPRI_MED))) {

			mp->b_datap->db_type = M_PROTO;
			mp->b_band = ord ? 0 : 1;	/* expedite */
			p = ((struct T_optdata_ind *) mp->b_wptr)++;
			p->PRIM_type = T_OPTDATA_IND;
			p->DATA_flag = (more ? T_ODF_MORE : 0) | (ord ? 0 : T_ODF_EX);
			p->OPT_length = opt_len;
			p->OPT_offset = opt_len ? sizeof(*p) : 0;

			/* indicate options */
			if (sp->i_flags & TF_SCTP_RECVOPT) {
				oh = ((struct t_opthdr *) mp->b_wptr)++;
				oh->len = str_len;
				oh->level = T_INET_SCTP;
				oh->name = T_SCTP_PPI;
				oh->status = T_SUCCESS;
				*((t_uscalar_t *) mp->b_wptr)++ = ppi;

				oh = ((struct t_opthdr *) mp->b_wptr)++;
				oh->len = str_len;
				oh->level = T_INET_SCTP;
				oh->name = T_SCTP_SID;
				oh->status = T_SUCCESS;
				*((t_uscalar_t *) mp->b_wptr)++ = sid;

				oh = ((struct t_opthdr *) mp->b_wptr)++;
				oh->len = str_len;
				oh->level = T_INET_SCTP;
				oh->name = T_SCTP_SSN;
				oh->status = T_SUCCESS;
				*((t_uscalar_t *) mp->b_wptr)++ = ssn;

				oh = ((struct t_opthdr *) mp->b_wptr)++;
				oh->len = str_len;
				oh->level = T_INET_SCTP;
				oh->name = T_SCTP_TSN;
				oh->status = T_SUCCESS;
				*((t_uscalar_t *) mp->b_wptr)++ = tsn;
			}
			mp->b_cont = dp;
			putnext(sp->rq, mp);
			return (0);
		}
		seldom();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}

/*
 *  T_ADDR_ACK	    27 - address acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_addr_ack(sctp_t * sp)
{
	mblk_t *mp;
	struct T_addr_ack *p;
	struct sctp_saddr *ss = sp->saddr;
	struct sctp_daddr *sd = sp->daddr;
	size_t loc_len = sp->sanum ? sizeof(sp->sport) + sp->sanum * sizeof(ss->saddr) : 0;
	size_t rem_len = sp->danum ? sizeof(sp->dport) + sp->danum * sizeof(sd->daddr) : 0;
	if ((mp = allocb(sizeof(*p) + loc_len + rem_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((struct T_addr_ack *) 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 (ss)
			*((typeof(sp->sport) *) mp->b_wptr)++ = sp->sport;
		for (; ss; ss = ss->next)
			*((typeof(ss->saddr) *) mp->b_wptr)++ = ss->saddr;
		if (sd)
			*((typeof(sp->dport) *) mp->b_wptr)++ = sp->dport;
		for (; sd; sd = sd->next)
			*((typeof(sd->daddr) *) mp->b_wptr)++ = sd->daddr;
		putnext(sp->rq, mp);
		return (0);
	}
	seldom();
	return (-ENOBUFS);
}

#if 0
/*
 *  T_CAPABILITY_ACK ?? - protocol capability ack
 *  -----------------------------------------------------------------
 */
STATIC int t_capability_ack(sctp_t * sp, ulong caps)
{
	mblk_t *mp;
	struct T_capability_ack *p;
	uint caps = (acceptor ? TC1_ACCEPTOR : 0) | (info ? TC1_INFO : 0);
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((struct T_capability_ack *) mp->b_wptr)++;
		p->PRIM_type = T_CAPABILITY_ACK;
		p->CAP_bits1 = caps;
		p->ACCEPTOR_id = (caps & TC1_ACCEPTOR) ? (ulong) sp->rq : 0;
		if (caps & TC1_INFO) {
			p->INFO_ack.PRIM_type = T_INFO_ACK;
			p->INFO_ack.TSDU_size = sp->tsdu;
			p->INFO_ack.ETSDU_size = sp->etsdu;
			p->INFO_ack.CDATA_size = sp->cdata;
			p->INFO_ack.DDATA_size = sp->ddata;
			p->INFO_ack.ADDR_size = sp->addlen;
			p->INFO_ack.OPT_size = sp->optlen;
			p->INFO_ack.TIDU_size = sp->tidu;
			p->INFO_ack.SERV_type = sp->stype;
			p->INFO_ack.CURRENT_state = sp->i_state;
			p->INFO_ack.PROVIDER_flag = sp->ptype;
		} else
			bzero(&p->INFO_ack, sizeof(p->INFO_ack));
		putnext(sp->rq, mp);
		return (0);
	}
	return (-ENOBUFS);
}
#endif

/*
 *  NOTES:- TPI cannot do data acknowledgements, resets or retrieval.  Data
 *  acknowledgements and retrieval are different forms of the same service.
 *  For data acknowledgement, use the NPI interface.  For reset support (SCTP
 *  Restart indication different from SCTP CDI), use the NPI interface.
 */
STATIC int sctp_t_conn_ind(sctp_t * sp, mblk_t * cp)
{
//ptrace(("sp = %x, CONN_IND: seq = %x\n", (uint)sp, (uint)cp));
	return t_conn_ind(sp, cp);
}
STATIC int sctp_t_conn_con(sctp_t * sp)
{
//ptrace(("sp = %x, CONN_CONF\n", (uint)sp));
	return t_conn_con(sp);
}
STATIC int sctp_t_data_ind(sctp_t * sp, uint32_t ppi, uint16_t sid, uint16_t ssn, uint32_t tsn, uint ord,
			   uint more, mblk_t * dp)
{
//ptrace(("sp = %x, DATA_IND: ppi=%u,sid=%u,ssn=%u,tsn=%u,ord=%u,more=%u\n", (uint)sp, ppi,sid,ssn,tsn,ord,more));
	if (sp->i_flags & TF_SCTP_RECVOPT)
		return t_optdata_ind(sp, ppi, sid, ssn, tsn, ord, more, dp);
	if (ord)
		return t_data_ind(sp, more, dp);
	else
		return t_exdata_ind(sp, more, dp);
}
STATIC int sctp_t_discon_ind(sctp_t * sp, ulong orig, long reason, mblk_t * cp)
{
//ptrace(("sp = %x, DISCON_IND\n", (uint)sp));
	(void) orig;
	return t_discon_ind(sp, reason, cp);
}
STATIC int sctp_t_ordrel_ind(sctp_t * sp)
{
//ptrace(("sp = %x, ORDREL_IND\n", (uint)sp));
	return t_ordrel_ind(sp);
}
STATIC struct sctp_ifops t_ops = {
	sctp_t_conn_ind,
	sctp_t_conn_con,
	sctp_t_data_ind,
	NULL,
	NULL,
	NULL,
	sctp_t_discon_ind,
	sctp_t_ordrel_ind,
	NULL,
	NULL
};

/*
 *  =========================================================================
 *
 *  SCTP T-User --> T-Provider Primitives (Request and Response)
 *
 *  =========================================================================
 *  These represent primitive requests and responses from the Transport Provider Interface
 *  (TPI) transport user.  Each of these requests or responses invoked a protocol action
 *  depending on the current state of the provider.
 */

/*
 *  T_CONN_REQ		 0 - TC requeset
 *  -------------------------------------------------------------------------
 *  We have received a connection request from the user.  We use the characteristics of the
 *  primitive and the options on the stream to formulate and INIT message and send it to the
 *  peer.  We acknowledge the success or failure to starting this process to the user.  Once
 *  the process is started, a successful connection will conclude with COOKIE_ACK message
 *  that will generate a T_CONN_CON.  If the init process times out or there are other errors
 *  associated with establishing the SCTP association, they will be returned via the
 *  T_DISCON_IND primitive.
 *
 *  The time-sequence diagrams look like this:
 *
 *                       |                            |                      
 *   T_CONN_REQ -------->|---+ TS_WACK_CREQ           |                      
 *                       |   |                        |                      
 *   T_ERROR_ACK <-------|<--+ TS_IDLE                |                      
 *                       |                            |                      
 *                       |           INIT             |                      
 *   T_CONN_REQ -------->|---+----------------------->|---+                  
 *                       |   | TS_WACK_CREQ           |   |                  
 *   T_OK_ACK <----------|<--+ TS_WCON_CREQ           |   |  Listener Stream 
 *                       |                            |   |  w/ Options      
 *                       |           ABORT            |   |                  
 *   T_DISCON_IND <------|<----TS_IDLE----------------|<--+                  
 *                       |    Timeout, other problem  |                      
 *                       |                            |                      
 *                       |           INIT             |                      
 *   T_CONN_REQ --+----->|---+----------------------->|---+                  
 *                |      |   | TS_WACK_CREQ           |   |                  
 *   T_OK_ACK <----------|<--+ TS_WCON_CREQ           |   |  Listener Stream 
 *                |      |                            |   |  w/ Options      
 *                |      |         INIT ACK           |   |                  
 *                |  +---|<---------------------------|<--+                  
 *                |  |   |       COOKIE ECHO          |                      
 *           DATA +--+-->|--------------------------->|----------> T_CONN_IND
 *                       |                            |                      
 *                       |       COOKIE ACK           |                      
 *   T_CONN_CON <--------|<---------------------------|<---------- T_CONN_RES
 *                       |     TS_DATA_XFER           |                      
 *                       |                            |                      
 *                       |                            |                      
 */

STATIC int t_conn_req(sctp_t * sp, mblk_t * mp)
{
	int err = -EFAULT;
	struct sctp_addr *a;
	size_t anum;
	const struct T_conn_req *p = (struct T_conn_req *) mp->b_rptr;
	if (sp->i_state != TS_IDLE)
		goto outstate;
	sp->i_state = TS_WACK_CREQ;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
		goto badopt;
	a = (struct sctp_addr *) (mp->b_rptr + p->DEST_offset);
	anum = (p->DEST_length - sizeof(a->port)) / sizeof(a->addr[0]);
	if (mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length)
		goto badaddr;
	if ((!anum || p->DEST_length != sizeof(a->port) + anum * sizeof(a->addr[0])) || !a->port)
		goto badaddr;
	if (sp->cred.cr_uid != 0 && a->port < 1024)
		goto access;
	{
		struct t_opthdr *rto = NULL;
		struct t_opthdr *hb = NULL;
		/* address per-association options */
		if (p->OPT_length) {
			unsigned char *op = mp->b_rptr + p->OPT_offset;
			unsigned char *oe = op + p->OPT_length;
			struct t_opthdr *oh = (struct t_opthdr *) op;
			err = 0;
			for (; op + sizeof(*oh) <= oe && oh->len >= sizeof(*oh) && op + oh->len <= oe;
			     op += oh->len, oh = (struct t_opthdr *) op) {
				t_scalar_t val = *((t_scalar_t *) (oh + 1));
				if (oh->level == T_INET_SCTP)
					switch (oh->name) {
					case T_SCTP_ISTREAMS:
						sp->max_istr = val ? val : 1;
						continue;
					case T_SCTP_OSTREAMS:
						sp->req_ostr = val ? val : 1;
						continue;
					case T_SCTP_SID:
						val = min(val, 0);
						val = max(val, sp->n_ostr);
						sp->sid = val;
						continue;
					case T_SCTP_PPI:
						sp->ppi = val;
						continue;
					case T_SCTP_SACK_DELAY:
						val = min(val, 10);
						sp->max_sack = val;
						continue;
					case T_SCTP_PATH_MAX_RETRANS:
						val = min(val, 0);
						sp->rtx_path = val;
						continue;
					case T_SCTP_ASSOC_MAX_RETRANS:
						val = min(val, 0);
						sp->max_retrans = val;
						continue;
					case T_SCTP_HEARTBEAT_ITVL:
						val = min(val, 10);
						sp->hb_itvl = val;
						continue;
					case T_SCTP_RTO_INITIAL:
						val = min(val, 10);
						sp->rto_ini = val;
						continue;
					case T_SCTP_RTO_MIN:
						val = min(val, 10);
						val = min(val, sp->rto_max);
						sp->rto_min = val;
						continue;
					case T_SCTP_RTO_MAX:
						val = min(val, 10);
						val = min(val, sp->rto_min);
						sp->rto_max = val;
						continue;
					case T_SCTP_HB:
						hb = oh;
						continue;
					case T_SCTP_RTO:
						rto = oh;
						continue;
					}
				err = TBADOPT;
				goto error;
			}
			if (op != oe)
				err = TBADOPT;
			if (err)
				goto error;
		}
#if 0
		/* allocate addresses now */
		if ((err = sctp_alloc_daddrs(sp, a->port, a->addr, anum)))
			goto error;
		/* address per-destination options */
		if (rto) {
			struct sctp_daddr
			*sd;
			t_sctp_rto_t *op = (t_sctp_rto_t *) rto->value;
			t_sctp_rto_t *oe = (t_sctp_rto_t *) (((caddr_t) rto) + rto->len);
			for (; op + 1 <= oe; op++) {
				if ((sd = sctp_find_daddr(sp, op->rto_dest))) {
					sd->rto = op->rto_initial;
					sd->rto_min = op->rto_min;
					sd->rto_max = op->rto_max;
					sd->max_retrans = op->max_retrans;
					continue;
				}
				err = TBADOPT;
				goto error;
			}
			if (op != oe)
				err = TBADOPT;
			if (err)
				goto error;
		}
		if (hb) {
			struct sctp_daddr
			*sd;
			t_sctp_hb_t *op = (t_sctp_hb_t *) hb->value;
			t_sctp_hb_t *oe = (t_sctp_hb_t *) (((caddr_t) hb) + hb->len);
			for (; op + 1 <= oe; op++) {
				if ((sd = sctp_find_daddr(sp, op->hb_dest))) {
					sd->hb_onoff = op->hb_onoff;
					sd->hb_itvl = op->hb_itvl;
					continue;
				}
				err = TBADOPT;
				goto error;
			}
			if (op != oe)
				err = TBADOPT;
			if (err)
				goto error;
		}
#endif
	}
	if ((err = sctp_conn_req(sp, a->port, a->addr, anum, mp->b_cont)))
		goto error;
	mp->b_cont = NULL;	/* absorbed mp->b_cont */
	return t_ok_ack(sp, T_CONN_REQ, 0, 0);
      access:
	seldom();
	err = TACCES;
	goto error;		/* no permission for requested address */
      badaddr:
	seldom();
	err = TBADADDR;
	goto error;		/* address is unusable */
      badopt:
	seldom();
	err = TBADOPT;
	goto error;		/* options are unusable */
      einval:
	seldom();
	err = -EINVAL;
	goto error;		/* invalid message format */
      outstate:
	seldom();
	err = TOUTSTATE;
	goto error;		/* would place interface out of state */
      error:
	seldom();
	return t_error_ack(sp, T_CONN_REQ, err);
}

/*
 *  T_CONN_RES		 1 - Accept previous connection indication
 *  -----------------------------------------------------------------
 */
/*
 *  IMPLEMENTATION NOTE:- Sequence numbers are actually the address of the mblk which
 *  contains the COOKIE-ECHO chunk and contains the cookie as a connection indication.  To
 *  find if a particular sequence number is valid, we walk the connection indication queue
 *  looking for a mblk with the same address as the sequence number.  Sequence numbers are
 *  only valid on the stream for which the connection indication is queued.
 */
STATIC mblk_t *t_seq_check(sctp_t * sp, ulong seq)
{
	mblk_t *mp = bufq_head(&sp->conq);
	for (; mp && mp != (mblk_t *) seq; mp = mp->b_next) ;
	usual(mp);
	return (mp);
}

sctp_t *sctp_t_list;
STATIC sctp_t *t_tok_check(ulong acceptor)
{
	sctp_t *ap;
	queue_t *aq = (queue_t *) acceptor;
	for (ap = sctp_t_list; ap && ap->rq != aq; ap = ap->next) ;
	usual(ap);
	return (ap);
}
STATIC int t_conn_res(sctp_t * sp, mblk_t * mp)
{
	int err = 0;
	mblk_t *cp;
	sctp_t *ap;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_conn_res *p = (struct T_conn_res *) mp->b_rptr;
	if (sp->i_state != TS_WRES_CIND)
		goto outstate;
	sp->i_state = TS_WACK_CRES;
	if (mlen < sizeof(*p))
		goto einval;
	if (mlen < p->OPT_offset + p->OPT_length)
		goto badopt;
	if (!(cp = t_seq_check(sp, p->SEQ_number)))
		goto badseq;
	if (!(ap = t_tok_check(p->ACCEPTOR_id)) || (ap != sp && !((1 << ap->i_state) & (TSF_UNBND | TSF_IDLE))))
		goto badf;
	if (ap->i_state == TS_IDLE && ap->conind)
		goto resqlen;
	/* protect at least r00t streams from users */
	if (sp->cred.cr_uid != 0 && ap->cred.cr_uid != sp->cred.cr_uid)
		goto access;
	{
		uint ap_oldstate = ap->i_state;
		struct t_opthdr *rto = NULL;
		struct t_opthdr *hb = NULL;
		/* address per-association options */
		if (p->OPT_length) {
			unsigned char *op = mp->b_rptr + p->OPT_offset;
			unsigned char *oe = op + p->OPT_length;
			struct t_opthdr *oh = (struct t_opthdr *) op;
			for (; op + sizeof(*oh) <= oe && oh->len >= sizeof(*oh) && op + oh->len <= oe;
			     op += oh->len, oh = (struct t_opthdr *) op) {
				t_scalar_t val = *((t_scalar_t *) (oh + 1));
				if (oh->level == T_INET_SCTP)
					switch (oh->name) {
					case T_SCTP_SID:
						val = min(val, 0);
						val = max(val, sp->n_ostr);
						sp->sid = val;
						continue;
					case T_SCTP_PPI:
						sp->ppi = val;
						continue;
					case T_SCTP_SACK_DELAY:
						val = min(val, 10);
						sp->max_sack = val;
						continue;
					case T_SCTP_PATH_MAX_RETRANS:
						val = min(val, 0);
						sp->rtx_path = val;
						continue;
					case T_SCTP_ASSOC_MAX_RETRANS:
						val = min(val, 0);
						sp->max_retrans = val;
						continue;
					case T_SCTP_HEARTBEAT_ITVL:
						val = min(val, 10);
						sp->hb_itvl = val;
						continue;
					case T_SCTP_RTO_INITIAL:
						val = min(val, 10);
						sp->rto_ini = val;
						continue;
					case T_SCTP_RTO_MIN:
						val = min(val, 10);
						val = min(val, sp->rto_max);
						sp->rto_min = val;
						continue;
					case T_SCTP_RTO_MAX:
						val = min(val, 10);
						val = min(val, sp->rto_min);
						sp->rto_max = val;
						continue;
					case T_SCTP_HB:
						hb = oh;
						continue;
					case T_SCTP_RTO:
						rto = oh;
						continue;
					}
				goto badopt;
			}
			if (op != oe)
				goto badopt;
			if (err)
				goto error;
		}
#if 0
		/* address per-destination options */
		if (rto) {
			struct sctp_daddr
			*sd;
			t_sctp_rto_t *op = (t_sctp_rto_t *) rto->value;
			t_sctp_rto_t *oe = (t_sctp_rto_t *) (((caddr_t) rto) + rto->len);
			for (; op + 1 <= oe; op++) {
				if ((sd = sctp_find_daddr(sp, op->rto_dest))) {
					sd->rto = op->rto_initial;
					sd->rto_min = op->rto_min;
					sd->rto_max = op->rto_max;
					sd->max_retrans = op->max_retrans;
					continue;
				}
				goto badopt;
			}
			if (op != oe)
				goto badopt;
			if (err)
				goto error;
		}
		if (hb) {
			struct sctp_daddr
			*sd;
			t_sctp_hb_t *op = (t_sctp_hb_t *) hb->value;
			t_sctp_hb_t *oe = (t_sctp_hb_t *) (((caddr_t) hb) + hb->len);
			for (; op + 1 <= oe; op++) {
				if ((sd = sctp_find_daddr(sp, op->hb_dest))) {
					sd->hb_onoff = op->hb_onoff;
					sd->hb_itvl = op->hb_itvl;
					continue;
				}
				goto badopt;
			}
			if (op != oe)
				goto badopt;
			if (err)
				goto error;
		}
#endif
		ap->i_state = TS_DATA_XFER;
		if ((err = sctp_conn_res(sp, cp, ap, mp->b_cont))) {
			ap->i_state = ap_oldstate;
			goto error;
		}
		mp->b_cont = NULL;	/* absorbed mp->b_cont */
		return t_ok_ack(sp, T_CONN_RES, (ulong) cp, (ulong) ap);
	}
      access:
	seldom();
	err = TACCES;
	goto error;		/* no access to accepting queue */
      resqlen:
	seldom();
	err = TRESQLEN;
	goto error;		/* accepting queue is listening */
      badf:
	seldom();
	err = TBADF;
	goto error;		/* accepting queue id is invalid */
      badseq:
	seldom();
	err = TBADSEQ;
	goto error;		/* connection ind referenced is invalid */
      badopt:
	seldom();
	err = TBADOPT;
	goto error;		/* options are bad */
      einval:
	seldom();
	err = -EINVAL;
	goto error;		/* invalid primitive format */
      outstate:
	seldom();
	err = TOUTSTATE;
	goto error;		/* would place interface out of state */
      error:
	seldom();
	return t_error_ack(sp, T_CONN_RES, err);
}

/*
 *  T_DISCON_REQ	2 - TC disconnection request
 *  -----------------------------------------------------------------
 *
 *  The time-sequence diagrams for abortive disconnect looks like this:
 *
 *
 *  Refusing a connection indication:
 *
 *                       |                            |                      
 *                       |        COOKIE ECHO         |                      
 *  T_CONN_IND <---------|<---------------------------|                      
 *                       |     TS_WRES_CIND           |                      
 *  T_DISCON_REQ ------->|---+                        |                      
 *                       |   | TS_WACK_DREQ7          |                      
 *  T_ERROR_ACK <--------|<--+                        |                      
 *                       |     TS_WRES_CIND           |                      
 *                       |                            |                      
 *                       |                            |                      
 *                       |        COOKIE ECHO         |                      
 *  T_CONN_IND <---------|<---------------------------|                      
 *    (DATA)             |     TS_WRES_CIND           |                      
 *                       |                            |                      
 *                       |       DATA + ABORT         |                      
 *  T_DISCON_REQ ------->|---+----------------------->|--------> T_DISCON_IND
 *    (DATA)             |   | TS_WACK_DREQ7          |            (DATA)    
 *                       |   |                        |                      
 *  T_OK_ACK <-----------|<--+ TS_IDLE                |                      
 *                       |                            |                      
 *                       |                            |                      
 *
 *  Disconnecting an established connection:
 *
 *                       |                            |                      
 *  T_DISCON_REQ ------->|---+                        |                      
 *    (DATA)             |   |                        |                      
 *  T_ERROR_ACK <--------|<--+                        |                      
 *                       |                            |                      
 *                       |                            |                      
 *                       |                            |                      
 *                       |        DATA + ABORT        |                      
 *  T_DISCON_REQ ------->|---+----------------------->|--------> T_DISCON_IND
 *    (DATA)             |   |                        |            (DATA)    
 *  T_OK_ACK <-----------|<--+                        |                      
 *                       |                            |                      
 *                       |                            |                      
 *                       |                            |                      
 *                       |        DATA + ABORT        |                      
 *  T_DISCON_REQ ------->|---+-------------------+--->|--------> T_DATA_IND  
 *    (DATA)             |   |                   |    |                      
 *  T_OK_ACK <-----------|<--+                   +--->|--------> T_DISCON_IND
 *                       |                            |                      
 *
 *  Any data that is associated wtih the T_DISCON_REQ will be bundled as DATA chunks before
 *  the ABORT chunk in the mesage.  These DATA chunks will be sent out of order and
 *  unreliably on the default stream id and with the default payload protocol identifier in
 *  the hope that it makes it through to the other ULP before the ABORT chunk is processed.
 *
 */
STATIC int t_discon_req(sctp_t * sp, mblk_t * mp)
{
	int err;
	mblk_t *cp = NULL;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	struct T_discon_req *p = (struct T_discon_req *) mp->b_rptr;
	if (!
	    ((1 << sp->
	      i_state) & (TSF_WCON_CREQ | TSF_WRES_CIND | TSF_DATA_XFER | TSF_WIND_ORDREL | TSF_WREQ_ORDREL)))
		goto outstate;
	switch (sp->i_state) {
	case TS_WCON_CREQ:
		sp->i_state = TS_WACK_DREQ6;
		break;
	case TS_WRES_CIND:
		sp->i_state = TS_WACK_DREQ7;
		break;
	case TS_DATA_XFER:
		sp->i_state = TS_WACK_DREQ9;
		break;
	case TS_WIND_ORDREL:
		sp->i_state = TS_WACK_DREQ10;
		break;
	case TS_WREQ_ORDREL:
		sp->i_state = TS_WACK_DREQ11;
		break;
	default:
		goto outstate;
	}
	if (mlen < sizeof(*p))
		goto einval;
	if (sp->i_state == TS_WACK_DREQ7 && !(cp = t_seq_check(sp, p->SEQ_number)))
		goto badseq;
	if ((err = sctp_discon_req(sp, cp)))
		goto error;
	return t_ok_ack(sp, T_DISCON_REQ, p->SEQ_number, 0);
      badseq:
	seldom();
	err = TBADSEQ;
	goto error;		/* connection ind reference is invalid */
      einval:
	seldom();
	err = -EINVAL;
	goto error;		/* invalid primitive format */
      outstate:
	seldom();
	err = TOUTSTATE;
	goto error;		/* would place interface out of state */
      error:
	seldom();
	return t_error_ack(sp, T_DISCON_REQ, err);
}

/*
 *  T_DATA_REQ		3 - Connection-Mode data transfer request
 *  -----------------------------------------------------------------
 */
STATIC int t_error_reply(sctp_t * sp, int err)
{
	mblk_t *mp;
	switch (err) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (err);
	case 0:
	case 1:
	case 2:
		never();
		return (err);
	}
	if ((mp = allocb(2, BPRI_HI))) {
		mp->b_datap->db_type = M_ERROR;
		*(mp->b_wptr)++ = err < 0 ? -err : err;
		*(mp->b_wptr)++ = err < 0 ? -err : err;
		putnext(sp->rq, mp);
		return (0);
	}
	seldom();
	return (-ENOBUFS);
}
STATIC int t_write(sctp_t * sp, mblk_t * mp)
{
	int err;
	if (sp->i_state == TS_IDLE)
		goto discard;
	if (!((1 << sp->i_state) & (TSF_DATA_XFER | TSF_WREQ_ORDREL)))
		goto oustate;
	{
		ulong ppi = sp->ppi;
		ulong sid = sp->sid;
		ulong ord = 1;
		ulong more = 0;
		ulong rcpt = 0;
		if ((err = sctp_data_req(sp, ppi, sid, ord, more, rcpt, mp)))
			goto error;
		return (1);	/* absorbed */
	}
      oustate:
	seldom();
	err = -EPROTO;
	goto error;		/* would place interface out of state */
      discard:
	seldom();
	return (0);		/* ignore in idle state */
      error:
	seldom();
	return t_error_reply(sp, err);
}
STATIC int t_data_req(sctp_t * sp, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_data_req *p = (struct T_data_req *) mp->b_rptr;
	if (sp->i_state == TS_IDLE)
		goto discard;
	if (mlen < sizeof(*p))
		goto einval;
	if (!((1 << sp->i_state) & (TSF_DATA_XFER | TSF_WREQ_ORDREL)))
		goto oustate;
	{
		ulong ppi = sp->ppi;
		ulong sid = sp->sid;
		ulong ord = 1;
		ulong more = p->MORE_flag;
		ulong rcpt = 0;
		if ((err = sctp_data_req(sp, ppi, sid, ord, more, rcpt, mp->b_cont)))
			goto error;
		mp->b_cont = NULL;	/* absorbed mp->b_cont */
		return (0);
	}
      oustate:
	seldom();
	err = -EPROTO;
	goto error;		/* would place interface out of state */
      einval:
	seldom();
	err = -EINVAL;
	goto error;		/* invlaid primitive format */
      discard:
	seldom();
	return (0);		/* ignore in idle state */
      error:
	seldom();
	return t_error_reply(sp, err);
}

/*
 *  T_EXDATA_REQ	 4 - Expedited data request
 *  -----------------------------------------------------------------
 */
STATIC int t_exdata_req(sctp_t * sp, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_exdata_req *p = (struct T_exdata_req *) mp->b_rptr;
	if (sp->i_state == TS_IDLE)
		goto discard;
	if (mlen < sizeof(*p))
		goto einval;
	if (!((1 << sp->i_state) & (TSF_DATA_XFER | TSF_WREQ_ORDREL)))
		goto oustate;
	{
		ulong ppi = sp->ppi;
		ulong sid = sp->sid;
		ulong ord = 0;
		ulong more = p->MORE_flag;
		ulong rcpt = 0;
		if ((err = sctp_data_req(sp, ppi, sid, ord, more, rcpt, mp->b_cont)))
			goto error;
		mp->b_cont = NULL;	/* absorbed mp->b_cont */
		return (0);
	}
      oustate:
	seldom();
	err = -EPROTO;
	goto error;		/* would place interface out of state */
      einval:
	seldom();
	err = -EINVAL;
	goto error;		/* invlaid primitive format */
      discard:
	seldom();
	return (0);
      error:
	seldom();
	return t_error_reply(sp, err);
}

/*
 *  T_INFO_REQ		 5 - Information Request
 *  -----------------------------------------------------------------
 */
STATIC int t_info_req(sctp_t * sp, mblk_t * mp)
{
	(void) mp;
	return t_info_ack(sp);
}

/*
 *  T_BIND_REQ		 6 - Bind a TS user to a transport address
 *  -----------------------------------------------------------------
 */
STATIC int t_bind_req(sctp_t * sp, mblk_t * mp)
{
	int err;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_bind_req *p = (struct T_bind_req *) mp->b_rptr;
	if (sp->i_state != TS_UNBND)
		goto outstate;
	sp->i_state = TS_WACK_BREQ;
	if (mlen < sizeof(*p))
		goto einval;
	{
		struct sctp_addr *a = (struct sctp_addr *) (mp->b_rptr + p->ADDR_offset);
		size_t anum = (p->ADDR_length - sizeof(a->port)) / sizeof(a->addr[0]);
		if ((mlen < p->ADDR_offset + p->ADDR_length) ||
		    (p->ADDR_length != sizeof(*a) + anum * sizeof(a->addr[0])))
			goto badaddr;
		/* we don't allow wildcards just yet */
		if (!anum || (!a->port && !(a->port = sctp_get_port())))
			goto noaddr;
		if (sp->cred.cr_uid != 0 && a->port < 1024)
			goto acces;
		if ((err = sctp_bind_req(sp, a->port, a->addr, anum, p->CONIND_number)))
			goto error;
		return t_bind_ack(sp);
	}
      acces:
	seldom();
	err = TACCES;
	goto error;		/* no permission for requested address */
      noaddr:
	seldom();
	err = TNOADDR;
	goto error;		/* cound not allocatea address */
      badaddr:
	seldom();
	err = TBADADDR;
	goto error;		/* address is invalid */
      einval:
	seldom();
	err = -EINVAL;
	goto error;		/* invalid primitive format */
      outstate:
	seldom();
	err = TOUTSTATE;
	goto error;		/* would place interface out of state */
      error:
	seldom();
	return t_error_ack(sp, T_BIND_REQ, err);
}

/*
 *  T_UNBIND_REQ	 7 - Unbind TS user from transport address
 *  -----------------------------------------------------------------
 */
STATIC int t_unbind_req(sctp_t * sp, mblk_t * mp)
{
	int err;
	const struct T_unbind_req *p = (struct T_unbind_req *) mp->b_rptr;
	(void) p;
	if (sp->i_state != TS_IDLE)
		goto outstate;
	sp->i_state = TS_WACK_UREQ;
	if ((err = sctp_unbind_req(sp)))
		goto error;
	return t_ok_ack(sp, T_UNBIND_REQ, 0, 0);
      outstate:
	seldom();
	err = TOUTSTATE;
	goto error;		/* would place interface out of state */
      error:
	seldom();
	return t_error_ack(sp, T_UNBIND_REQ, err);
}

/*
 *  T_OPTMGMT_REQ	 9 - Options management request
 *  -----------------------------------------------------------------
 *  The T_OPTMGMT_REQ is responsible for establishing options while the stream
 *  is in the T_IDLE state.  When the stream is bound to a local address using
 *  the T_BIND_REQ, the settings of options with end-to-end significance will
 *  have an affect on how the driver response to an INIT with INIT-ACK for SCTP.
 *  For example, the bound list of addresses is the list of addresses that will
 *  be sent in the INIT-ACK.  The number of inbound streams and outbound streams
 *  are the numbers that will be used in the INIT-ACK.
 */

/*
 *  Errors:
 *
 *  TACCES:	the user did not have proper permissions for the user of the requested
 *		options.
 *
 *  TBADFLAG:	the flags as specified were incorrect or invalid.
 *
 *  TBADOPT:	the options as specified were in an incorrect format, or they contained
 *		invalid information.
 *
 *  TOUTSTATE:	the primitive would place the transport interface out of state.
 *
 *  TNOTSUPPORT: this prmitive is not supported.
 *
 *  TSYSERR:	a system error has occured and the UNIX system error is indicated in the
 *		primitive.
 */

STATIC int t_optmgmt_req(sctp_t * sp, mblk_t * mp)
{
	int err = 0;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_optmgmt_req *p = (struct T_optmgmt_req *) mp->b_rptr;
#ifdef TS_WACK_OPTREQ
	if (sp->i_state == TS_IDLE)
		sp->i_state = TS_WACK_OPTREQ;
#endif
	if (mlen < sizeof(*p))
		goto einval;
	if (mlen < p->OPT_offset + p->OPT_length)
		goto badopt;
	{
		ulong flags = p->MGMT_flags;
		size_t opt_len = p->OPT_length;
		unsigned char *opt_ptr = mp->b_rptr + p->OPT_offset;
		struct sctp_opts ops = { 0L, NULL, };
		struct sctp_opts *opsp = NULL;
		if (opt_len) {
			if ((err = sctp_parse_opts(&ops, opt_ptr, opt_len)))
				goto error;
			opsp = &ops;
		}
		switch (flags) {
		case T_CHECK:
			sctp_check_opts(sp, opsp);
			return t_optmgmt_ack(sp, flags, opsp);
		case T_NEGOTIATE:
			sctp_negotiate_opts(sp, opsp);
			return t_optmgmt_ack(sp, flags, opsp);
		case T_DEFAULT:
			/* return defaults for the specified options */
		case T_CURRENT:
			return t_optmgmt_ack(sp, flags, opsp);
		default:
			goto badflag;
		}
	}
      badflag:
	seldom();
	err = TBADFLAG;
	goto error;		/* bad options flags */
      badopt:
	seldom();
	err = TBADOPT;
	goto error;		/* options were invalid */
      einval:
	seldom();
	err = -EINVAL;
	goto error;		/* invalid primitive format */
      error:
	seldom();
	return t_error_ack(sp, T_OPTMGMT_REQ, err);
}

/*
 *  T_ORDREL_REQ	10 - TS user is finished sending
 *  -----------------------------------------------------------------
 *
 *  The time-sequence diagrams look like this:
 *
 *                       |                            |                      
 *                  TS_DATA_XFER                 TS_DATA_XFER                
 *                       |            DATA            |                      
 *  T_DATA_REQ --------->|--------------------------->|----------> T_DATA_IND
 *                       |            DATA            |                      
 *  T_DATA_REQ --------->|--------------------------->|----------> T_DATA_IND
 *                       |            DATA            |                      
 *  T_DATA_REQ --------->|--------------------------->|----------> T_DATA_IND
 *                       |            DATA            |                      
 *  T_DATA_REQ --------->|--------------------------->|----------> T_DATA_IND
 *                       |                            |                      
 *  T_ORDREL_REQ ------->|<---------- SACK -----------|                      
 *                 TS_WIND_ORDREL                     |                      
 *                       |<---------- SACK -----------|                      
 *                       |<---------- SACK -----------|                      
 *                       |<---------- SACK -----------|                      
 *                       |         SHUTDOWN           |                      
 *                       |--------------------------->|--------> T_ORDREL_IND
 *                       |                      TS_WREQ_ORDREL               
 *                       |           DATA             |                      
 *  T_DATA_IND <---------|<---+-----------------------|<---------- T_DATA_REQ
 *                       |    |    SHUTDOWN           |                      
 *                       |    +---------------------->|                      
 *                       |           DATA             |                      
 *  T_DATA_IND <---------|<---+-----------------------|<---------- T_DATA_REQ
 *                       |    |    SHUTDOWN           |                      
 *                       |    +---------------------->|                      
 *                       |                            |                      
 *                       |       SHUTDOWN ACK         |                      
 *  T_ORDREL_IND <-------|<---+-----------------------|<-------- T_ORDREL_REQ
 *                    TS_IDLE |                    TS_IDLE                   
 *                       |    |SHUTDOWN COMPLETE      |                      
 *                       |    +---------------------->|                      
 *                       |                            |                      
 *  =====================|============================|======================
 *                       |                            |                      
 *  T_ORDREL_REQ ------->|                            |<-------- T_ORDREL_REQ
 *                 TS_WIND_ORDREL               TS_WIND_ORDREL               
 *                       |<---------- SACK -----------|                      
 *                       |----------- SACK ---------->|                      
 *                       |<---------- SACK -----------|                      
 *                       |----------- SACK ---------->|                      
 *                       |                            |                      
 *                       |<-------- SHUTDOWN ---------|                      
 *                       |--------- SHUTDOWN -------->|                      
 *  T_ORDREL_IND <-------|<--+--- SHUTDOWN ACK -------|                      
 *                       |---|--- SHUTDOWN ACK ---+-->|--------> T_ORDREL_IND
 *                       |   +- SHUTDOWN COMPLETE-|-->|                      
 *                       |<-----SHUTDOWN COMPLETE-+   |                      
 *                       |                            |                      
 *                       |                            |                      
 *                       |                            |                      
 *                       |                            |                      
 */
STATIC int t_ordrel_req(sctp_t * sp, mblk_t * mp)
{
	int err;
	if (!((1 << sp->i_state) & (TSF_DATA_XFER | TSF_WREQ_ORDREL)))
		goto outstate;
	switch (sp->i_state) {
	case TS_DATA_XFER:
		sp->i_state = TS_WIND_ORDREL;
		break;
	case TS_WREQ_ORDREL:
		sp->i_state = TS_IDLE;
		break;
	}
	if ((err = sctp_ordrel_req(sp)))
		goto error;
	return (0);
      outstate:
	seldom();
	err = -EPROTO;
	goto error;		/* would place interface out of state */
      error:
	seldom();
	return t_error_reply(sp, err);
}

/*
 *  T_OPTDATA_REQ	24- data with options request
 *  -----------------------------------------------------------------
 *  Basically the purpose of this for SCTP is to be able to set the SCTP Unordered Bit
 *  (U-bit), Payload Protocol Identifier (PPI) and Stream Id (sid) associated with the data
 *  message.  If not specified, each option will reduce to the current default settings.
 */
STATIC int t_optdata_req(sctp_t * sp, mblk_t * mp)
{
	int err;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_optdata_req *p = (struct T_optdata_req *) mp->b_rptr;
	if (sp->i_state == TS_IDLE)
		goto discard;
	if (mlen < sizeof(*p))
		goto einval;
	if ((p->OPT_length && (mlen < p->OPT_offset + p->OPT_length)))
		goto einval;
	if (!((1 << sp->i_state) & (TSF_DATA_XFER | TSF_WREQ_ORDREL)))
		goto outstate;
	{
		ulong ppi = sp->ppi;
		ulong sid = sp->sid;
		ulong ord = !(p->DATA_flag & T_ODF_EX);
		ulong more = (p->DATA_flag & T_ODF_MORE);
		ulong rcpt = 0;
		if (p->OPT_length) {
			unsigned char *op = mp->b_rptr + p->OPT_offset;
			unsigned char *oe = op + p->OPT_length;
			struct t_opthdr *oh = (struct t_opthdr *) op;
			for (; op + sizeof(*oh) <= oe && oh->len >= sizeof(*oh)
			     && op + oh->len <= oe; op += oh->len, oh = (struct t_opthdr *) op) {
				if (oh->level == T_INET_SCTP)
					switch (oh->name) {
					case T_SCTP_SID:
						sid = (*((t_scalar_t *) (oh + 1))) & 0xffff;
						continue;
					case T_SCTP_PPI:
						ppi = (*((t_scalar_t *) (oh + 1))) & 0xffffffff;
						continue;
					}
			}
		}
		if ((err = sctp_data_req(sp, ppi, sid, ord, more, rcpt, mp->b_cont)))
			goto error;
		mp->b_cont = NULL;	/* absorbed mp->b_cont */
		return (0);
	}
      outstate:
	seldom();
	err = -EPROTO;
	goto error;		/* would place interface out of state */
      einval:
	seldom();
	err = -EINVAL;
	goto error;		/* invalid primitive format */
      discard:
	seldom();
	return (0);		/* ignore the idle state */
      error:
	seldom();
	return t_error_reply(sp, err);
}

/*
 *  T_ADDR_REQ		25 - address request
 *  -----------------------------------------------------------------
 */
STATIC int t_addr_req(sctp_t * sp, mblk_t * mp)
{
	(void) mp;
	return t_addr_ack(sp);
}

/* 
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sctp_t_w_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	sctp_t *sp = (sctp_t *) q->q_ptr;
	ulong oldstate = sp->i_state;
	switch ((prim = *((ulong *) mp->b_rptr))) {
	case T_CONN_REQ:
		rtn = t_conn_req(sp, mp);
		break;
	case T_CONN_RES:
		rtn = t_conn_res(sp, mp);
		break;
	case T_DISCON_REQ:
		rtn = t_discon_req(sp, mp);
		break;
	case T_DATA_REQ:
		rtn = t_data_req(sp, mp);
		break;
	case T_EXDATA_REQ:
		rtn = t_exdata_req(sp, mp);
		break;
	case T_INFO_REQ:
		rtn = t_info_req(sp, mp);
		break;
	case T_BIND_REQ:
		rtn = t_bind_req(sp, mp);
		break;
	case T_UNBIND_REQ:
		rtn = t_unbind_req(sp, mp);
		break;
	case T_OPTMGMT_REQ:
		rtn = t_optmgmt_req(sp, mp);
		break;
	case T_ORDREL_REQ:
		rtn = t_ordrel_req(sp, mp);
		break;
	case T_OPTDATA_REQ:
		rtn = t_optdata_req(sp, mp);
		break;
	case T_ADDR_REQ:
		rtn = t_addr_req(sp, mp);
		break;
	default:
		rtn = -EOPNOTSUPP;
		break;
	}
	if (rtn < 0) {
		rare();
		sp->i_state = oldstate;
	}
	return (rtn);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sctp_t_w_data(queue_t * q, mblk_t * mp)
{
	sctp_t *sp = (sctp_t *) q->q_ptr;
	return t_write(sp, mp);
}
STATIC int sctp_t_r_data(queue_t * q, mblk_t * mp)
{
	sctp_t *sp = (sctp_t *) q->q_ptr;
	return sctp_recv_msg(sp, mp);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sctp_t_r_error(queue_t * q, mblk_t * mp)
{
	sctp_t *sp = (sctp_t *) q->q_ptr;
	rare();
	return sctp_recv_err(sp, mp);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 *  This is complete flush handling in both directions.  Standard stuff.
 */
STATIC int sctp_t_m_flush(queue_t *q, mblk_t *mp, const uint8_t mflag, const uint8_t oflag)
{
	if (*mp->b_rptr & mflag) {
		if (*mp->b_rptr & FLUSHBAND)
			flushband(q, mp->b_rptr[1], FLUSHALL);
		else
			flushq(q, FLUSHALL);
		if (q->q_next) {
			putnext(q, mp);
			return (QR_ABSORBED);
		}
		*mp->b_rptr &= ~mflag;
	}
	if (*mp->b_rptr & oflag) {
		queue_t *oq = q->q_other;
		if (*mp->b_rptr & FLUSHBAND)
			flushband(oq, mp->b_rptr[1], FLUSHALL);
		else
			flushq(oq, FLUSHALL);
		if (oq->q_next) {
			putnext(oq, mp);
			return (QR_ABSORBED);
		}
		*mp->b_rptr &= ~oflag;
	}
	return (0);
}
STATIC int sctp_t_w_flush(queue_t *q, mblk_t *mp)
{
	return sctp_t_m_flush(q, mp, FLUSHW, FLUSHR);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  Other messages (e.g. M_IOCACK)
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sctp_t_r_other(queue_t * q, mblk_t * mp)
{
	sctp_t *sp = SCTP_PRIV(q);
	rare();
	cmn_err(CE_WARN, "Unsupported block type %d on RD(q) %d\n", mp->b_datap->db_type, sp->cminor);
	putnext(q, mp);
	return (QR_ABSORBED);
}
STATIC int sctp_t_w_other(queue_t * q, mblk_t * mp)
{
	sctp_t *sp = SCTP_PRIV(q);
	rare();
	cmn_err(CE_WARN, "Unsupported block type %d on WR(q) %d\n", mp->b_datap->db_type, sp->cminor);
	return (-EOPNOTSUPP);
}

/* 
 *  =========================================================================
 *
 *  STREAMS PUTQ and SRVQ routines
 *
 *  =========================================================================
 */
/* 
 *  TPI Write Message
 */
STATIC INLINE INT sctp_t_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sctp_t_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return sctp_t_w_proto(q, mp);
	case M_FLUSH:
		return sctp_t_w_flush(q, mp);
	default:
		return sctp_t_w_other(q, mp);
	}
}

/* 
 *  IP Read Message
 */
STATIC INLINE INT sctp_t_r_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sctp_t_r_data(q, mp);
	case M_ERROR:
		return sctp_t_r_error(q, mp);
	default:
		return sctp_t_r_other(q, mp);
	}
}

/* 
 *  PUTQ Put Routine
 *  -----------------------------------
 */
STATIC INLINE int sctp_t_putq(queue_t * q, mblk_t * mp, int (*proc) (queue_t *, mblk_t *))
{
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	if (mp->b_datap->db_type >= QPCTL && !q->q_count && !sctp_trylock(q)) {
		int rtn;
		switch ((rtn = proc(q, mp))) {
		case QR_DONE:
			freemsg(mp);
		case QR_ABSORBED:
			break;
		case QR_TRIMMED:
			freeb(mp);
			break;
		case QR_LOOP:
			if (!q->q_next) {
				qreply(q, mp);
				break;
			}
		case QR_PASSALONG:
			if (q->q_next) {
				putnext(q, mp);
				break;
			}
			rtn = -EOPNOTSUPP;
		default:
			ptrace(("ERROR: (q dropping) %d\n", rtn));
			freemsg(mp);
			break;
		case QR_DISABLE:
			putq(q, mp);
			rtn = 0;
			break;
		case QR_PASSFLOW:
			if (mp->b_datap->db_type >= QPCTL || canputnext(q)) {
				putnext(q, mp);
				break;
			}
		case -ENOBUFS:
		case -EBUSY:
		case -ENOMEM:
		case -EAGAIN:
			putq(q, mp);
			break;
		}
		sctp_unlock(q);
		return (rtn);
	} else {
		seldom();
		putq(q, mp);
		return (0);
	}
}

/* 
 *  SRVQ Put Routine
 *  -----------------------------------
 */
STATIC INLINE int sctp_t_srvq(queue_t * q, int (*proc) (queue_t *, mblk_t *))
{
	ensure(q, return (-EFAULT));
	if (!sctp_waitlock(q)) {
		int rtn = 0;
		mblk_t *mp;
		while ((mp = getq(q))) {
			switch ((rtn = proc(q, mp))) {
			case QR_DONE:
				freemsg(mp);
			case QR_ABSORBED:
				continue;
			case QR_TRIMMED:
				freeb(mp);
				continue;
			case QR_LOOP:
				if (!q->q_next) {
					qreply(q, mp);
					continue;
				}
			case QR_PASSALONG:
				if (q->q_next) {
					putnext(q, mp);
					continue;
				}
				rtn = -EOPNOTSUPP;
			default:
				ptrace(("ERROR: (q dropping) %d\n", rtn));
				freemsg(mp);
				continue;
			case QR_DISABLE:
				ptrace(("ERROR: (q disabling) %d\n", rtn));
				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:	/* proc must schedule bufcall */
			case -EBUSY:	/* proc must fail canput */
			case -ENOMEM:	/* proc must schedule re-enable */
			case -EAGAIN:	/* proc must schedule re-enable */
				if (mp->b_datap->db_type < QPCTL) {
					ptrace(("ERROR: (q stalled) %d\n", rtn));
					putbq(q, mp);
					break;
				}
				if (mp->b_datap->db_type == M_PCPROTO) {
					mp->b_datap->db_type = M_PROTO;
					mp->b_band = 255;
					putq(q, mp);
					break;
				}
				ptrace(("ERROR: (q dropping) %d\n", rtn));
				freemsg(mp);
				continue;
			}
			break;
		}
		sctp_unlock(q);
		return (rtn);
	}
	return (-EAGAIN);
}
STATIC INT sctp_t_rput(queue_t * q, mblk_t * mp)
{
	return (INT) sctp_t_putq(q, mp, &sctp_t_r_prim);
}
STATIC INT sctp_t_rsrv(queue_t * q)
{
	return (INT) sctp_t_srvq(q, &sctp_t_r_prim);
}
STATIC INT sctp_t_wput(queue_t * q, mblk_t * mp)
{
	return (INT) sctp_t_putq(q, mp, &sctp_t_w_prim);
}
STATIC INT sctp_t_wsrv(queue_t * q)
{
	return (INT) sctp_t_srvq(q, &sctp_t_w_prim);
}

/* 
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 */
sctp_t *sctp_t_list = NULL;

STATIC int sctp_t_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	int cmajor = getmajor(*devp);
	int cminor = getminor(*devp);
	sctp_t *sp, **spp = &sctp_t_list;
	(void) crp;
	if (q->q_ptr != NULL)
		return (0);	/* already open */
	if (sflag == MODOPEN || WR(q)->q_next) {
		rare();
		return (EIO);	/* can't open as module */
	}
	if (!cminor)
		sflag = CLONEOPEN;
	if (sflag == CLONEOPEN)
		cminor = 1;
	for (; *spp && (*spp)->cmajor < cmajor; spp = &(*spp)->next) ;
	for (; *spp && cminor <= SCTP_NMINOR; spp = &(*spp)->next) {
		ushort dminor = (*spp)->cminor;
		if (cminor < dminor)
			break;
		if (cminor == dminor) {
			if (sflag != CLONEOPEN) {
				rare();
				return (ENXIO);	/* requested device in use */
			}
			cminor++;
		}
	}
	if (cminor > SCTP_NMINOR) {
		rare();
		return (ENXIO);
	}
	*devp = makedevice(cmajor, cminor);
	if (!(sp = sctp_alloc_priv(q, spp, cmajor, cminor, &t_ops))) {
		rare();
		return (ENOMEM);
	}
	return (0);
}
STATIC int sctp_t_close(queue_t * q, int flag, cred_t * crp)
{
	(void) flag;
	(void) crp;
	sctp_free_priv(q);
	return (0);
}

/* 
 *  =========================================================================
 *
 *  LiS Module Initialization
 *
 *  =========================================================================
 */
void sctp_t_init(void)
{
	int cmajor;
	if ((cmajor = lis_register_strdev(SCTP_T_CMAJOR, &sctp_t_info, SCTP_NMINOR, sctp_t_minfo.mi_idname)) < 0) {
		sctp_t_minfo.mi_idnum = 0;
		rare();
		cmn_err(CE_NOTE, "sctp: couldn't register driver\n");
		return;
	}
	sctp_t_minfo.mi_idnum = cmajor;
}
void sctp_t_term(void)
{
	if (sctp_t_minfo.mi_idnum) {
		if ((sctp_t_minfo.mi_idnum = lis_unregister_strdev(sctp_t_minfo.mi_idnum))) {
			sctp_t_minfo.mi_idnum = 0;
			rare();
			cmn_err(CE_WARN, "sdt: couldn't unregister driver!\n");
		}
	}
};


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

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

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