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/sigtran/ua_upper_t.c


File /code/strss7/drivers/sigtran/ua_upper_t.c



#ident "@(#) $RCSfile: ua_upper_t.c,v $ $Name:  $($Revision: 0.8.2.2 $) $Date: 2003/04/03 19:51:09 $"

static char const ident[] =
    "$RCSfile: ua_upper_t.c,v $ $Name:  $($Revision: 0.8.2.2 $) $Date: 2003/04/03 19:51:09 $";

#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/xti.h>
#include <sys/xti_osi.h>
#include <sys/xti_ss7.h>
#include <sys/xti_sl.h>
#include <sys/xti_mtp.h>
#include <sys/xti_sccp.h>
#include <sys/xti_tcap.h>

#include <sys/tpi.h>
#include <sys/tpi_sl.h>
#include <sys/tpi_mtp.h>
#include <sys/tpi_sccp.h>
#include <sys/tpi_tcap.h>

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

#include "ua_data.h"
#include "ua.h"
#include "ua_msgs.h"
#include "ua_upper.h"

/* 
 *  =========================================================================
 *
 *  Option handling functions
 *
 *  =========================================================================
 */

/* 
 *  Size and Build Default options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  Either builds the default options requested or builds all default options.
 */
STATIC size_t ss7_default_opts_size(dp_t * dp, ss7_opts_t * ops)
{
	size_t len = 0;
	const size_t hlen = sizeof(struct t_opthdr);
	const size_t olen = hlen + sizeof(t_scalar_t);
	register uint level = dp->prot.level;
	register uint result = ops ? ops->result : -1UL;
	if (result & TF_SS7_PVAR)
		len += olen;
	if (result & TF_SS7_MPLEV)
		len += olen;
	if (result & TF_SS7_DEBUG)
		len += olen;
	if (level == T_SS7_SL) {
		if (result & TF_SS7_PCR)
			len += olen;
		if (result & TF_SS7_HSL)
			len += olen;
		if (result & TF_SS7_XSN)
			len += olen;
	} else {
		if (result & TF_SS7_CLUSTER)
			len += olen;
		if (result & TF_SS7_SEQ_CTRL)
			len += olen;
		if (result & TF_SS7_PRIORITY)
			len += olen;
	}
	if (level == T_SS7_SCCP || level == T_SS7_TCAP) {
		if (result & TF_SS7_PCLASS)
			len += olen;
		if (result & TF_SS7_IMPORTANCE)
			len += olen;
		if (result & TF_SS7_RET_ERROR)
			len += olen;
	}
	return (len);
}

STATIC void ss7_build_default_opts(dp_t * dp, ss7_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);
	register uint level = dp->prot.level;
	register uint result = ops ? ops->result : -1UL;
	if (result & TF_SS7_PVAR) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = level;
		oh->name = T_SS7_PVAR;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = ss7_default_pvar;
	}
	if (result & TF_SS7_MPLEV) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = level;
		oh->name = T_SS7_MPLEV;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = ss7_default_mplev;
	}
	if (result & TF_SS7_DEBUG) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = level;
		oh->name = T_SS7_DEBUG;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = ss7_default_debug;
	}
	if (level == T_SS7_SL) {
		if (result & TF_SS7_PCR) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_PCR;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = ss7_default_pcr;
		}
		if (result & TF_SS7_HSL) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_HSL;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = ss7_default_hsl;
		}
		if (result & TF_SS7_XSN) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_XSN;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = ss7_default_xsn;
		}
	} else {
		if (result & TF_SS7_CLUSTER) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_CLUSTER;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = ss7_default_clust;
		}
		if (result & TF_SS7_SEQ_CTRL) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_SEQ_CTRL;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = ss7_default_seq_ctrl;
		}
		if (result & TF_SS7_PRIORITY) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_PRIORITY;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = ss7_default_priority;
		}
	}
	if (level == T_SS7_SCCP || level == T_SS7_TCAP) {
		if (result & TF_SS7_PCLASS) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_PCLASS;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = ss7_default_pclass;
		}
		if (result & TF_SS7_IMPORTANCE) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_IMPORTANCE;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = ss7_default_importance;
		}
		if (result & TF_SS7_RET_ERROR) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_RET_ERROR;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = ss7_default_ret_error;
		}
	}
}

/* 
 *  Size and Build Current options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  Either builds the current options requested or builds all current options.
 */
STATIC size_t ss7_current_opts_size(dp_t * dp, ss7_opts_t * ops)
{
	size_t len = 0;
	const size_t hlen = sizeof(struct t_opthdr);
	const size_t olen = hlen + sizeof(t_scalar_t);
	register uint level = dp->prot.level;
	register uint result = ops ? ops->result : -1UL;
	if (result & TF_SS7_PVAR)
		len += olen;
	if (result & TF_SS7_MPLEV)
		len += olen;
	if (result & TF_SS7_DEBUG)
		len += olen;
	if (level == T_SS7_SL) {
		if (result & TF_SS7_PCR)
			len += olen;
		if (result & TF_SS7_HSL)
			len += olen;
		if (result & TF_SS7_XSN)
			len += olen;
	} else {
		if (result & TF_SS7_CLUSTER)
			len += olen;
		if (result & TF_SS7_SEQ_CTRL)
			len += olen;
		if (result & TF_SS7_PRIORITY)
			len += olen;
	}
	if (level == T_SS7_SCCP || level == T_SS7_TCAP) {
		if (result & TF_SS7_PCLASS)
			len += olen;
		if (result & TF_SS7_IMPORTANCE)
			len += olen;
		if (result & TF_SS7_RET_ERROR)
			len += olen;
	}
	return (len);
}

STATIC void ss7_build_current_opts(dp_t * dp, ss7_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);
	register uint level = dp->prot.level;
	register uint result = ops ? ops->result : -1UL;
	if (result & TF_SS7_PVAR) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = level;
		oh->name = T_SS7_PVAR;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = dp->prot.pvar;
	}
	if (result & TF_SS7_MPLEV) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = level;
		oh->name = T_SS7_MPLEV;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = (dp->flags & T_SS7_POPT_MPLEV) ? T_YES : T_NO;
	}
	if (result & TF_SS7_DEBUG) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = level;
		oh->name = T_SS7_DEBUG;
		oh->status = T_SUCCESS;
		*((t_scalar_t *) * p)++ = (dp->flags & T_SS7_POPT_DEBUG) ? T_YES : T_NO;
	}
	if (level == T_SS7_SL) {
		if (result & TF_SS7_PCR) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_PCR;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = (dp->flags & T_SS7_POPT_PCR) ? T_YES : T_NO;
		}
		if (result & TF_SS7_HSL) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_HSL;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = (dp->flags & T_SS7_POPT_HSL) ? T_YES : T_NO;
		}
		if (result & TF_SS7_XSN) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_XSN;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = (dp->flags & T_SS7_POPT_XSN) ? T_YES : T_NO;
		}
	} else {
		if (result & TF_SS7_CLUSTER) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_CLUSTER;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = (dp->flags & T_SS7_POPT_CLUSTER) ? T_YES : T_NO;
		}
		if (result & TF_SS7_SEQ_CTRL) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_SEQ_CTRL;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = dp->prot.seq;
		}
		if (result & TF_SS7_PRIORITY) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_PRIORITY;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = dp->prot.prior;
		}
	}
	if (level == T_SS7_SCCP || level == T_SS7_TCAP) {
		if (result & TF_SS7_PCLASS) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_PCLASS;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = dp->prot.pclass;
		}
		if (result & TF_SS7_IMPORTANCE) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_IMPORTANCE;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = dp->prot.imp;
		}
		if (result & TF_SS7_RET_ERROR) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_RET_ERROR;
			oh->status = T_SUCCESS;
			*((t_scalar_t *) * p)++ = dp->prot.reterr;
		}
	}
}

/* 
 *  Size and Build Checked or Negotiated options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  Builds the checked or negotiated options.
 */
STATIC size_t ss7_set_opts_size(dp_t * dp, ss7_opts_t * ops)
{
	size_t len = 0;
	const size_t hlen = sizeof(struct t_opthdr);
	const size_t olen = hlen + sizeof(t_scalar_t);
	register uint level = dp->prot.level;
	register uint result = ops ? ops->result : -1UL;
	if (result & TF_SS7_PVAR)
		len += olen;
	if (result & TF_SS7_MPLEV)
		len += olen;
	if (result & TF_SS7_DEBUG)
		len += olen;
	if (level == T_SS7_SL) {
		if (result & TF_SS7_PCR)
			len += olen;
		if (result & TF_SS7_HSL)
			len += olen;
		if (result & TF_SS7_XSN)
			len += olen;
	} else {
		if (result & TF_SS7_CLUSTER)
			len += olen;
		if (result & TF_SS7_SEQ_CTRL)
			len += olen;
		if (result & TF_SS7_PRIORITY)
			len += olen;
	}
	if (level == T_SS7_SCCP || level == T_SS7_TCAP) {
		if (result & TF_SS7_PCLASS)
			len += olen;
		if (result & TF_SS7_IMPORTANCE)
			len += olen;
		if (result & TF_SS7_RET_ERROR)
			len += olen;
	}
	return (len);
}

STATIC void ss7_build_set_opts(dp_t * dp, ss7_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);
	register uint level = dp->prot.level;
	register uint flags = ops->flags;
	register uint result = ops->result;
	if (flags & TF_SS7_PVAR) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = level;
		oh->name = T_SS7_PVAR;
		oh->status = result & TF_SS7_PVAR ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = ops->pvar;
	}
	if (flags & TF_SS7_MPLEV) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = level;
		oh->name = T_SS7_MPLEV;
		oh->status = result & TF_SS7_MPLEV ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = ops->mplev;
	}
	if (flags & TF_SS7_DEBUG) {
		oh = ((struct t_opthdr *) *p)++;
		oh->len = olen;
		oh->level = level;
		oh->name = T_SS7_DEBUG;
		oh->status = result & TF_SS7_DEBUG ? T_SUCCESS : T_FAILURE;
		*((t_scalar_t *) * p)++ = ops->debug;
	}
	if (level == T_SS7_SL) {
		if (flags & TF_SS7_PCR) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_PCR;
			oh->status = result & TF_SS7_PCR ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p)++ = ops->pcr;
		}
		if (flags & TF_SS7_HSL) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_HSL;
			oh->status = result & TF_SS7_HSL ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p)++ = ops->hsl;
		}
		if (flags & TF_SS7_XSN) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_XSN;
			oh->status = result & TF_SS7_XSN ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p)++ = ops->xsn;
		}
	} else {
		if (flags & TF_SS7_CLUSTER) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_CLUSTER;
			oh->status = result & TF_SS7_CLUSTER ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p)++ = ops->clust;
		}
		if (flags & TF_SS7_SEQ_CTRL) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_SEQ_CTRL;
			oh->status = result & TF_SS7_SEQ_CTRL ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p)++ = ops->seq;
		}
		if (flags & TF_SS7_PRIORITY) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_PRIORITY;
			oh->status = result & TF_SS7_PRIORITY ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p)++ = ops->prior;
		}
	}
	if (level == T_SS7_SCCP || level == T_SS7_TCAP) {
		if (flags & TF_SS7_PCLASS) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_PCLASS;
			oh->status = result & TF_SS7_PCLASS ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p)++ = ops->pclass;
		}
		if (flags & TF_SS7_IMPORTANCE) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_IMPORTANCE;
			oh->status = result & TF_SS7_IMPORTANCE ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p)++ = ops->imp;
		}
		if (flags & TF_SS7_RET_ERROR) {
			oh = ((struct t_opthdr *) *p)++;
			oh->len = olen;
			oh->level = level;
			oh->name = T_SS7_RET_ERROR;
			oh->status = result & TF_SS7_RET_ERROR ? T_SUCCESS : T_FAILURE;
			*((t_scalar_t *) * p)++ = ops->reterr;
		}
	}
}

/* 
 *  Size and Build options.
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC size_t ss7_opts_size(ulong flags, dp_t * dp, ss7_opts_t * ops)
{
	switch (flags) {
	case T_CHECK:
	case T_NEGOTIATE:
		if (ops)
			return ss7_set_opts_size(dp, ops);
	case T_DEFAULT:
		return ss7_default_opts_size(dp, ops);
	case T_CURRENT:
		return ss7_current_opts_size(dp, ops);
	}
	return (0);
}
STATIC void ss7_build_opts(ulong flags, dp_t * dp, ss7_opts_t * ops, unsigned char **p)
{
	switch (flags) {
	case T_CHECK:
	case T_NEGOTIATE:
		if (ops)
			return ss7_build_set_opts(dp, ops, p);
	case T_DEFAULT:
		return ss7_build_default_opts(dp, ops, p);
	case T_CURRENT:
		return ss7_build_current_opts(dp, ops, p);
	}
	return;
}

/* 
 *  Parse options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC int ss7_parse_opts(ss7_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_SS7_SL:
		case T_SS7_MTP:
		case T_SS7_SCCP:
		case T_SS7_TCAP:
			switch (oh->name) {
			case T_TCAP_PVAR:
				ops->pvar = *(ulong *) (oh + 1);
				continue;
			case T_TCAP_MPLEV:
				ops->mplev = *(ulong *) (oh + 1);
				continue;
			case T_TCAP_DEBUG:
				ops->debug = *(ulong *) (oh + 1);
				continue;
			case T_SS7_PCR:
				ops->pcr = *(ulong *) (oh + 1);
				continue;
			case T_SS7_HSL:
				ops->hsl = *(ulong *) (oh + 1);
				continue;
			case T_SS7_XSN:
				ops->xsn = *(ulong *) (oh + 1);
				continue;
			case T_TCAP_CLUSTER:
				ops->clust = *(ulong *) (oh + 1);
				continue;
			case T_TCAP_SEQ_CTRL:
				ops->seq = *(ulong *) (oh + 1);
				continue;
			case T_TCAP_PRIORITY:
				ops->prior = *(ulong *) (oh + 1);
				continue;
			case T_SS7_PCLASS:
				ops->pclass = *(ulong *) (oh + 1);
				continue;
			case T_SS7_IMPORTANCE:
				ops->imp = *(ulong *) (oh + 1);
				continue;
			case T_SS7_RET_ERROR:
				ops->reterr = *(ulong *) (oh + 1);
				continue;
			}
			continue;
		}
		return (TBADOPT);
	}
	if (opt_ptr != opt_end)
		return (TBADOPT);
	return (0);
}

/* 
 *  Negotiate options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC void ss7_negotiate_opts(dp_t * dp, ss7_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->flags & TF_SS7_PVAR) {
		/* not writeable */
	}
	if (ops->flags & TF_SS7_MPLEV) {
		/* not writeable */
	}
	if (ops->flags & TF_SS7_PCR) {
		/* not writeable */
	}
	if (ops->flags & TF_SS7_HSL) {
		/* not writeable */
	}
	if (ops->flags & TF_SS7_XSN) {
		/* not writeable */
	}
	if (ops->flags & TF_SS7_CLUSTER) {
		/* not writeable */
	}
	if (ops->debug && ops->debug->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->debug + 1);
		switch (*val) {
		case T_YES:
			dp->flags |= SCCP_FLAG_DEBUG;
			break;
		case T_NO:
			dp->flags &= ~SCCP_FLAG_DEBUG;
			break;
		}
		ops->flags |= TF_SS7_DEBUG;
	}
	if (ops->seq && ops->seq->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->seq + 1);
		fixme(("Do some sanity checks here...\n"));
		dp->prot.seq = *val;
		ops->flags |= TF_SS7_SEQ_CTRL;
	}
	if (ops->prior && ops->prior->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->prior + 1);
		fixme(("Do some sanity checks here...\n"));
		dp->prot.prior = *val;
		ops->flags |= TF_SS7_PRIORITY;
	}
	return;
}

/* 
 *  Check options
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC void ss7_check_opts(dp_t * dp, ss7_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->pvar) {
		/* not writeable */
	}
	if (ops->mplev) {
		/* not writeable */
	}
	if (ops->pcr) {
		/* not writeable */
	}
	if (ops->hsl) {
		/* not writeable */
	}
	if (ops->xsn) {
		/* not writeable */
	}
	if (ops->clust) {
		/* not writeable */
	}
	if (ops->debug && ops->debug->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->debug + 1);
		switch (*val) {
		case T_YES:
		case T_NO:
			ops->flags |= TF_SS7_DEBUG;
			break;
		}
	}
	if (ops->seq && ops->seq->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->seq + 1);
		(void) val;
		fixme(("Do some sanity checks here...\n"));
		ops->flags |= TF_SS7_SEQ_CTRL;
	}
	if (ops->prior && ops->prior->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->prior + 1);
		(void) val;
		fixme(("Do some sanity checks here...\n"));
		ops->flags |= TF_SS7_PRIORITY;
	}
	if (ops->pvar) {
		/* not writeable */
	}
	if (ops->mplev) {
		/* not writeable */
	}
	if (ops->pcr) {
		/* not writeable */
	}
	if (ops->hsl) {
		/* not writeable */
	}
	if (ops->xsn) {
		/* not writeable */
	}
	if (ops->clust) {
		/* not writeable */
	}
	if (ops->debug && ops->debug->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->debug + 1);
		switch (*val) {
		case T_YES:
		case T_NO:
			ops->flags |= TF_SS7_DEBUG;
			break;
		}
	}
	if (ops->seq && ops->seq->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->seq + 1);
		(void) val;
		fixme(("Do some sanity checks here...\n"));
		ops->flags |= TF_SS7_SEQ_CTRL;
	}
	if (ops->prior && ops->prior->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->prior + 1);
		(void) val;
		fixme(("Do some sanity checks here...\n"));
		ops->flags |= TF_SS7_PRIORITY;
	}
	if (ops->pvar) {
		/* not writeable */
	}
	if (ops->mplev) {
		/* not writeable */
	}
	if (ops->pcr) {
		/* not writeable */
	}
	if (ops->hsl) {
		/* not writeable */
	}
	if (ops->xsn) {
		/* not writeable */
	}
	if (ops->clust) {
		/* not writeable */
	}
	if (ops->debug && ops->debug->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->debug + 1);
		switch (*val) {
		case T_YES:
		case T_NO:
			ops->flags |= TF_SS7_DEBUG;
			break;
		}
	}
	if (ops->seq && ops->seq->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->seq + 1);
		(void) val;
		fixme(("Do some sanity checks here...\n"));
		ops->flags |= TF_SS7_SEQ_CTRL;
	}
	if (ops->prior && ops->prior->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->prior + 1);
		(void) val;
		fixme(("Do some sanity checks here...\n"));
		ops->flags |= TF_SS7_PRIORITY;
	}
	if (ops->pvar) {
		/* not writeable */
	}
	if (ops->mplev) {
		/* not writeable */
	}
	if (ops->pcr) {
		/* not writeable */
	}
	if (ops->hsl) {
		/* not writeable */
	}
	if (ops->xsn) {
		/* not writeable */
	}
	if (ops->clust) {
		/* not writeable */
	}
	if (ops->debug && ops->debug->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->debug + 1);
		switch (*val) {
		case T_YES:
		case T_NO:
			ops->flags |= TF_TCAP_DEBUG;
			break;
		}
	}
	if (ops->seq && ops->seq->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->seq + 1);
		(void) val;
		fixme(("Do some sanity checks here...\n"));
		ops->flags |= TF_TCAP_SEQ_CTRL;
	}
	if (ops->prior && ops->prior->len >= olen) {
		t_scalar_t *val = (t_scalar_t *) (ops->prior + 1);
		(void) val;
		fixme(("Do some sanity checks here...\n"));
		ops->flags |= TF_TCAP_PRIORITY;
	}
	return;
}

/* 
 *  =========================================================================
 *
 *  SCCP T-Provider --> T-User Primitives (Indication, Confirmation and Ack)
 *
 *  =========================================================================
 */
/* 
 *  T_CONN_IND          11 - connection indication
 *  -----------------------------------------------------------------
 *  We get the information from the CR message in the connection indication.
 *  We queue the CR message (complete with decode parameter block) itself as
 *  the connection indication.  The sequence number is really just a pointer
 *  to the first mblk_t in the received CR message.
 */
STATIC INLINE int t_conn_ind(queue_t * q, struct sockaddr *src, mblk_t * cp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	sccp_msg_t *m = (typeof(m)) cp->b_rptr;
	struct T_conn_ind *p;
	struct t_opthdr *oh;
	const size_t src_len = sizeof(*src);
	const size_t opt_len = sizeof(*oh) + sizeof(t_scalar_t);
	const size_t msg_len = sizeof(*p) + PAD4(src_len) + opt_len;
	if ((1 << dp->state) & ~(TSF_IDLE | TSF_WRES_CIND))
		goto efault;
	if (bufq_length(&dp->conq) >= dp->conind)
		goto erestart;
	if (!canputnext(q))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_CONN_IND;
		p->SRC_length = src_len;
		p->SRC_offset = src_len ? sizeof(*p) : 0;
		p->OPT_length = opt_len;
		p->OPT_offset = opt_len ? sizeof(*p) + PAD4(src_len) : 0;
		p->SEQ_number = (ulong) cp;
		if (src_len) {
			bcopy(src, mp->b_wptr, src_len);
			mp->b_wptr += PAD4(src_len);
		}
		oh = ((typeof(oh)) mp->b_wptr)++;
		oh->len = opt_len;
		oh->level = T_SS7_SCCP;
		oh->name = T_SS7_PCLASS;
		oh->status = T_SUCCESS;
		*((t_uscalar_t *) mp->b_wptr)++ = m->pclass;
		bufq_queue(&dp->conq, cp);
		dp->state = TS_WRES_CIND;
		putnext(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	goto error;
      erestart:
	err = -ERESTART;
	ptrace(("ERROR: too many conn inds\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_CONN_CON          12 - connection confirmation
 *  -----------------------------------------------------------------
 *  The only options with end-to-end significance that are negotiated is the
 *  protocol class.
 */
STATIC INLINE int t_conn_con(queue_t * q, uint pcls, uint flags, struct sockaddr *res, mblk_t * dp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_conn_con *p;
	struct t_opthdr *oh;
	size_t res_len = res ? sizeof(*res) : 0;
	size_t opt_len = sizeof(*oh) + sizeof(t_scalar_t);
	size_t msg_len = sizeof(*p) + PAD4(res_len) + opt_len;
	if (dp->state != TS_WCON_CREQ)
		goto efault;
	if (!canputnext(q))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		mp->b_datap->db_type = M_PROTO;
		mp->b_band = 1;	/* expedite */
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_CONN_CON;
		p->RES_length = res_len;
		p->RES_offset = res_len ? sizeof(*p) : 0;
		p->OPT_length = opt_len;
		p->OPT_offset = opt_len ? sizeof(*p) + PAD4(res_len) : 0;
		if (res_len) {
			bcopy(res, mp->b_wptr, res_len);
			mp->b_wptr += PAD4(res_len);
		}
		{
			oh = ((typeof(oh)) mp->b_wptr)++;
			oh->len = opt_len;
			oh->level = T_SS7_SCCP;
			oh->name = T_SS7_PCLASS;
			oh->status = T_SUCCESS;
			*((t_uscalar_t *) mp->b_wptr)++ = pcls;
		}
		dp->state = TS_DATA_XFER;
		putnext(q, mp);
		return (0);
	}
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_DISCON_IND        13 - disconnect indication
 *  -----------------------------------------------------------------
 *  We use the address of the mblk_t that contains the CR message as a
 *  SEQ_number for connection indications that are rejected with a disconnect
 *  indication as well.  We can use this to directly address the mblk in the
 *  connection indication bufq.
 */
STATIC INLINE int t_discon_ind(queue_t * q, long reason, mblk_t * seq, mblk_t * dp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_discon_ind *p;
	size_t msg_len = sizeof(*p);
	if ((1 << dp->
	     state) & ~(TSF_WCON_CREQ | TSF_WRES_CIND | TSF_DATA_XFER | TSF_WIND_ORDREL |
			TSF_WREQ_ORDREL))
		goto efault;
	if (!canputnext(q))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_DISCON_IND;
		p->DISCON_reason = reason;
		p->SEQ_number = (ulong) seq;
		if (seq) {
			bufq_unlink(&dp->conq, seq);
			freemsg(seq);
		}
		if (!bufq_length(&dp->conq))
			dp->state = TS_IDLE;
		else
			dp->state = TS_WRES_CIND;
		mp->b_cont = dp;
		putnext(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_DATA_IND          14 - data indication
 *  -----------------------------------------------------------------
 */
STATIC INLINE int t_data_ind(queue_t * q, ulong more, mblk_t * dp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_data_ind *p;
	size_t msg_len = sizeof(*p);
	if ((1 << dp->state) & ~(TSF_DATA_XFER | TSF_WIND_ORDREL))
		goto efault;
	if (!canputnext(q))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_DATA_IND;
		p->MORE_flag = more;
		mp->b_cont = dp;
		putnext(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_EXDATA_IND        15 - expedited data indication
 *  -----------------------------------------------------------------
 */
STATIC INLINE int t_exdata_ind(queue_t * q, ulong more, mblk_t * dp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_exdata_ind *p;
	size_t msg_len = sizeof(*p);
	if ((1 << dp->state) & ~(TSF_DATA_XFER | TSF_WIND_ORDREL))
		goto efault;
	if (!canputnext(q))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		mp->b_datap->db_type = M_PROTO;
		mp->b_band = 1;	/* expedite */
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_EXDATA_IND;
		p->MORE_flag = more;
		mp->b_cont = dp;
		putnext(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_INFO_ACK          16 - information acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC INLINE int t_info_ack(queue_t * q)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_info_ack *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		uint serv = dp->pclass < 2 ? T_CLTS : T_COTS_ORD;
		uint etsdu = dp->pclass < 2 ? T_INVALID : dp->mtu;
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		fixme(("Still some things to double-check here\n"));
		p->PRIM_type = T_INFO_ACK;
		p->TSDU_size = T_INFINITE;	/* no limit on TSDU size */
		p->ETSDU_size = etsdu;	/* no concept of ETSDU size */
		p->CDATA_size = dp->mtu;	/* no concept of CDATA size */
		p->DDATA_size = dp->mtu;	/* no concept of DDATA size */
		p->ADDR_size = sizeof(struct sockaddr);	/* no limit on ADDR size */
		p->OPT_size = T_INFINITE;	/* no limit on OPTIONS size */
		p->TIDU_size = T_INFINITE;	/* no limit on TIDU size */
		p->SERV_type = serv;	/* COTS or CLTS */
		p->CURRENT_state = dp->i_state;
		p->PROVIDER_flag = XPG4_1 & ~T_SNDZERO;
		qreply(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_BIND_ACK          17 - bind acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_bind_ack(queue_t * q, struct sockaddr *add)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_bind_ack *p;
	size_t add_len = add ? sizeof(*add) : 0;
	size_t msg_len = sizeof(*p) + add_len;
	if (dp->i_state != TS_WACK_BREQ)
		goto efault;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_BIND_ACK;
		p->ADDR_length = add_len;
		p->ADDR_offset = add_len ? sizeof(*p) : 0;
		p->CONIND_number = dp->conind;
		if (add_len) {
			bcopy(add, mp->b_wptr, add_len);
			mp->b_wptr++;
		}
		dp->i_state = TS_IDLE;
		qreply(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_ERROR_ACK         18 - error acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_error_ack(queue_t * q, ulong prim, long error)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_error_ack *p;
	size_t msg_len = sizeof(*p);
	switch (error) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (error);
	case 0:
		never();
		return (error);
	}
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_ERROR_ACK;
	p->ERROR_prim = prim;
	p->TLI_error = error < 0 ? TSYSERR : error;
	p->UNIX_error = error < 0 ? -error : 0;
	if ((1 << dp->i_state) & (TS_WACK_OPTREQ | TS_WACK_UREQ |
				  TS_WACK_CREQ | TS_WACK_BREQ |
				  TS_WACK_CRES | TS_WACK_DREQ6 |
				  TS_WACK_DREQ7 | TS_WACK_DREQ9 | TS_WACK_DREQ10 | TS_WACK_DREQ11))
	{
		switch (dp->i_state) {
#ifdef TS_WACK_OPTREQ
		case TS_WACK_OPTREQ:
#endif
		case TS_WACK_UREQ:
		case TS_WACK_CREQ:
			dp->i_state = TS_IDLE;
			break;
		case TS_WACK_BREQ:
			dp->i_state = TS_UNBND;
			break;
		case TS_WACK_CRES:
			dp->i_state = TS_WRES_CIND;
			break;
		case TS_WACK_DREQ6:
			dp->i_state = TS_WCON_CREQ;
			break;
		case TS_WACK_DREQ7:
			dp->i_state = TS_WRES_CIND;
			break;
		case TS_WACK_DREQ9:
			dp->i_state = TS_DATA_XFER;
			break;
		case TS_WACK_DREQ10:
			dp->i_state = TS_WIND_ORDREL;
			break;
		case TS_WACK_DREQ11:
			dp->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.
	 */
	qreply(q, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_OK_ACK            19 - success acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_ok_ack(queue_t * q, ulong prim, ulong seq, ulong tok)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_ok_ack *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_OK_ACK;
	p->CORRECT_prim = prim;
	if ((1 << dp->i_state) & (TS_WACK_CREQ | TS_WACK_UREQ | TS_WACK_CRES |
				  TS_WACK_DREQ6 | TS_WACK_DREQ7 | TS_WACK_DREQ9 |
				  TS_WACK_DREQ10 | TS_WACK_DREQ11)) {
		switch (dp->i_state) {
		case TS_WACK_CREQ:
			dp->i_state = TS_WCON_CREQ;
			break;
		case TS_WACK_UREQ:
			dp->i_state = TS_UNBND;
			break;
		case TS_WACK_CRES:
		{
			queue_t *aq = (queue_t *) tok;
			dp_t *ap = UA_PRIV(aq);
			if (ap) {
				ap->i_state = TS_DATA_XFER;
				ap->pcb.rd_cleanup(aq);
				ap->pcb.wr_wakeup(aq);
			}
			if (seq) {
				bufq_unlink(&dp->conq, (mblk_t *) seq);
				freemsg((mblk_t *) seq);
			}
			if (aq != dp->rq) {
				if (bufq_length(&dp->conq))
					dp->i_state = TS_WRES_CIND;
				else
					dp->i_state = TS_IDLE;
			}
			break;
		}
		case TS_WACK_DREQ7:
			if (seq)
				bufq_unlink(&dp->conq, (mblk_t *) seq);
		case TS_WACK_DREQ6:
		case TS_WACK_DREQ9:
		case TS_WACK_DREQ10:
		case TS_WACK_DREQ11:
			if (bufq_length(&dp->conq))
				dp->i_state = TS_WRES_CIND;
			else
				dp->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.
			 */
		}
	}
	qreply(q, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_OPTMGMT_ACK       22 - options management acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_optmgmt_ack(queue_t * q, ulong flags, ss7_opts_t * ops)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_optmgmt_ack *p;
	size_t opt_len = ss7_opts_size(flags, dp, ops);
	size_t msg_len = sizeof(*p) + opt_len;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_OPTMGMT_ACK;
		p->OPT_length = opt_len;
		p->OPT_offset = opt_len ? sizeof(*p) : 0;
		p->MGMT_flags = flags;
		ss7_build_opts(flags, dp, ops, &mp->b_wptr);
#ifdef TS_WACK_OPTREQ
		if (dp->i_state == TS_WACK_OPTREQ)
			dp->i_state = TS_IDLE;
#endif
		qreply(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_ORDREL_IND        23 - orderly release indication
 *  -----------------------------------------------------------------
 */
STATIC INLINE int t_ordrel_ind(queue_t * q)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_ordrel_ind *p;
	size_t msg_len = sizeof(*p);
	if ((1 << dp->i_state) & ~(TSF_DATA_XFER | TSF_WIND_ORDREL))
		goto efault;
	if (!canputnext(q))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_ORDREL_IND;
		switch (dp->i_state) {
		case TS_DATA_XFER:
			dp->i_state = TS_WREQ_ORDREL;
			break;
		case TS_WIND_ORDREL:
			dp->i_state = TS_IDLE;
			break;
		}
		putnext(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_OPTDATA_IND   26 - data with options indication
 *  -----------------------------------------------------------------
 */
STATIC int
t_optdata_ind(queue_t * q, uint32_t ppi, uint16_t sid, uint16_t ssn,
	      uint32_t tsn, uint ord, uint more, mblk_t * dp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	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;
	size_t msg_len = sizeof(*p) + opt_len;
	if ((1 << dp->i_state) & ~(TSF_DATA_XFER | TSF_WREQ_ORDREL))
		goto efault;
	if (!canputnext(q))
		goto ebusy;
	if (dp->i_flags & TF_SCTP_RECVOPT)
		opt_len = 4 * str_len;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		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 (dp->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(q, mp);
		return (0);
	}
      efault:
	err = -EFAULT;
	ptrace(("ERROR: indication in wrong state\n"));
	goto error;
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: no buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: flow controlled\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_ADDR_ACK          27 - address acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_addr_ack(queue_t * q, struct sockaddr *loc, struct sockaddr *rem)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_addr_ack *p;
	size_t loc_len = loc ? sizeof(*loc) : 0;
	size_t rem_len = rem ? sizeof(*rem) : 0;
	size_t msg_len = sizeof(*p) + loc_len + rem_len;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		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 (loc_len) {
			bcopy(loc, mp->b_wptr, loc_len);
			mp->b_wptr += loc_len;
		}
		if (rem_len) {
			bcopy(rem, mp->b_wptr, rem_len);
			mp->b_wptr += rem_len;
		}
		qreply(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_CAPABILITY_ACK    ?? - protocol capability ack
 *  -----------------------------------------------------------------
 */
STATIC int t_capability_ack(queue_t * q, ulong caps)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_capability_ack *p;
	size_t msg_len = sizeof(*p);
	uint caps = (acceptor ? TC1_ACCEPTOR : 0) | (info ? TC1_INFO : 0);
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		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) RD(q) : 0;
		if (caps & TC1_INFO) {
			p->INFO_ack.PRIM_type = T_INFO_ACK;
			p->INFO_ack.TSDU_size = dp->tsdu;
			p->INFO_ack.ETSDU_size = dp->etsdu;
			p->INFO_ack.CDATA_size = dp->cdata;
			p->INFO_ack.DDATA_size = dp->ddata;
			p->INFO_ack.ADDR_size = dp->addlen;
			p->INFO_ack.OPT_size = dp->optlen;
			p->INFO_ack.TIDU_size = dp->tidu;
			p->INFO_ack.SERV_type = dp->stype;
			p->INFO_ack.CURRENT_state = dp->i_state;
			p->INFO_ack.PROVIDER_flag = dp->ptype;
		} else
			bzero(&p->INFO_ack, sizeof(p->INFO_ack));
		qreply(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_UNITDATA_IND      20 - Unitdata indication
 *  -----------------------------------------------------------------
 *  Note: If we cannot deliver the destination address in the option header or
 *  somewhere, it will not be possible to bind to multiple alias addresses,
 *  but will only permit us to bind to a single alias address.  This might or
 *  might not be a problem to the user.
 */
STATIC INLINE int t_unitdata_ind(queue_t * q, struct sockaddr *src, uint * seq, uint * prior,
				 mblk_t * dp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct t_opthdr *oh;
	const size_t olen = sizeof(*oh) + sizeof(t_scalar_t);
	struct T_unitdata_ind *p;
	size_t src_len = src ? sizeof(*src) : 0;
	size_t opt_len = (seq ? olen : 0) + (prior ? olen : 0);
	size_t msg_len = sizeof(*p) + src_len + opt_len;
	if (!canputnext(q))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_UNITDATA_IND;
		p->SRC_length = src_len;
		p->SRC_offset = src_len ? sizeof(*p) : 0;
		p->OPT_length = opt_len;
		p->OPT_offset = opt_len ? sizeof(*p) + src_len : 0;
		if (src_len) {
			bcopy(src, mp->b_wptr, src_len);
			mp->b_wptr += src_len;
		}
		if (opt_len) {
			if (seq) {
				oh = ((typeof(oh)) mp->b_wptr)++;
				oh->len = olen;
				oh->level = T_SS7_SCCP;
				oh->name = T_SS7_SEQ_CTRL;
				oh->status = T_SUCCESS;
				*((t_uscalar_t *) mp->b_wptr)++ = *seq;
			}
			if (prior) {
				oh = ((typeof(oh)) mp->b_wptr)++;
				oh->len = olen;
				oh->level = T_SS7_SCCP;
				oh->name = T_SS7_PRIORITY;
				oh->status = T_SUCCESS;
				*((t_uscalar_t *) mp->b_wptr)++ = *prior;
			}
		}
		putnext(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  T_UDERROR_IND       21 - Unitdata error indication
 *  -----------------------------------------------------------------
 *  This primitive indicates to the transport user that a datagram with the
 *  specified destination address and options produced an error.
 */
STATIC INLINE int
t_uderror_ind(queue_t * q, uint error, struct sockaddr *dst, uint * seq, uint * prior, mblk_t * dp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *mp;
	struct t_opthdr *oh;
	const size_t olen = sizeof(*oh) + sizeof(t_scalar_t);
	struct T_uderror_ind *p;
	size_t dst_len = dst ? sizeof(*dst) : 0;
	size_t opt_len = (seq ? olen : 0) + (prior ? olen : 0);
	size_t msg_len = sizeof(*p) + dst_len;
	if (!canputnext(OTHER(q)))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		mp->b_datap->db_type = M_PROTO;
		mp->b_band = 2;	/* XXX move ahead of data indications */
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_UDERROR_IND;
		p->DEST_length = dst_len;
		p->DEST_offset = dst_len ? sizeof(*p) : 0;
		p->OPT_length = opt_len;
		p->OPT_offset = opt_len ? sizeof(*p) + dst_len : 0;
		p->ERROR_type = error;
		if (dst_len) {
			bcopy(dst, mp->b_wptr, dst_len);
			mp->b_wptr += dst_len;
		}
		if (opt_len) {
			if (seq) {
				oh = ((typeof(oh)) mp->b_wptr)++;
				oh->len = olen;
				oh->level = T_SS7_SCCP;
				oh->name = T_SS7_SEQ_CTRL;
				oh->status = T_SUCCESS;
				*((t_uscalar_t *) mp->b_wptr)++ = *seq;
			}
			if (prior) {
				oh = ((typeof(oh)) mp->b_wptr)++;
				oh->len = olen;
				oh->level = T_SS7_SCCP;
				oh->name = T_SS7_PRIORITY;
				oh->status = T_SUCCESS;
				*((t_uscalar_t *) mp->b_wptr)++ = *prior;
			}
		}
		qreply(q, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  =========================================================================
 *
 *  STATE MACHINE Interface
 *
 *  =========================================================================
 */
STATIC int ss7_conn_ind(dp_t * dp, mblk_t * cp)
{
	return t_conn_ind(dp->rq, cp);
}
STATIC int ss7_conn_con(dp_t * dp, uint pcls, uint flags, struct sockaddr *res, mblk_t * dp)
{
	return t_conn_con(dp->rq, pcls, flags, res, dp);
}
STATIC int ss7_data_ind(dp_t * dp, uint exp, uint more, ss7_opts_t * opt, mblk_t * dp)
{
	if (!opt) {
		if (exp)
			return t_exdata_ind(dp->rq, more, dp);
		else
			return t_data_ind(dp->rq, more, dp);
	} else {
		return t_optdata_ind(dp->rq, exp, more, opt, dp);
	}
}
STATIC int ss7_datack_ind(dp_t * dp)
{
}
STATIC int ss7_discon_ind(dp_t * dp, ulong orig, long reason, struct sockaddr *res, mblk_t * cp,
			  mblk_t * dp)
{
	return t_discon_ind(dp->rq, reason, cp, dp);
}
STATIC int ss7_ordrel_ind(dp_t * dp)
{
	return t_ordrel_ind(dp->rq);
}
STATIC int
ss7_unitdata_ind(dp_t * dp, struct sockaddr *dst, struct sockaddr *src,
		 uint pcl, uint opt, uint imp, uint seq, uint pri, mblk_t * dp)
{
	return t_unitdata_ind(dp->rq, src, &seq, &pri, dp);
}
STATIC int
ss7_uderror_ind(dp_t * dp, uint cause, struct sockaddr *dst, struct sockaddr *src,
		uint pcl, uint opt, uint imp, uint seq, uint pri, mblk_t * dp)
{
	(void) src;
	return t_uderror_ind(dp->rq, cause, dst, &seq, &pri, dp);
}
STATIC struct ss7_ifops ss7_t_ops = {
	ss7_conn_ind,
	ss7_conn_con,
	ss7_data_ind,
	ss7_datack_ind,
	ss7_reset_ind,
	ss7_reset_con,
	ss7_discon_ind,
	ss7_ordrel_ind,
	ss7_unitdata_ind,
	ss7_uderror_ind
};
/* 
 *  =========================================================================
 *
 *  SCCP 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
 *  -------------------------------------------------------------------------
 */
STATIC int t_conn_req(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err = -EFAULT;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_conn_req *p = (typeof(p)) mp->b_rptr;
	if (dp->i_state != TS_IDLE)
		goto toutstate;
	dp->i_state = TS_WACK_CREQ;
	if (mlen < sizeof(*p))
		goto einval if (mlen < p->OPT_offset + p->OPT_length)
			goto tbadopt;
	struct sockaddr_ss7 *dst = (struct sockaddr_ss7 *) (mp->b_rptr + p->DEST_offset);
	size_t anum = (p->DEST_length - sizeof(dst->port)) / sizeof(dst->addr[0]);
	if ((mlen < p->DEST_offset + p->DEST_length)
	    || !anum || p->DEST_length != sizeof(dst->port) + anum * sizeof(dst->addr[0])
	    || !dst->port)
		goto tbadaddr;
	if (dp->cred.cr_uid != 0 && dst->port < 1024)
		goto tacces;
	{
		ss7_opts_t opt = { 0, };
		unsigned char *opt_ptr = mp->b_rptr + p->OPT_offset;
		size_t opt_len = p->OPT_length;
		if ((err = ss7_parse_opts(&opt, opt_ptr, opt_len)))
			goto error;
		if ((err = dp->pcb.conn_req(q, dst, &opt, mp->b_cont)))
			goto error;
		if ((err = t_ok_ack(q, T_CONN_REQ, 0, 0)))
			return (err);
		return (QR_TRIMMED);
	}
      tacces:
	err = TACCES;
	ptrace(("ERROR: no permission for requested address\n"));
	goto error;
      tbadaddr:
	err = TBADADDR;
	ptrace(("ERROR: address is unusable\n"));
	goto error;
      tbadopt:
	err = TBADOPT;
	ptrace(("ERROR: options are unusable\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid message format\n"));
	goto error;
      toutstate:
	err = TOUTSTATE;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:
	return t_error_ack(q, T_CONN_REQ, err);
}

/* 
 *  T_CONN_RES           1 - Accept previous connection indication
 *  -----------------------------------------------------------------
 */
/* 
 *  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(queue_t * q, ulong seq)
{
	dp_t *dp = UA_PRIV(q);
	mblk_t *mp = bufq_head(&dp->conq);
	for (; mp && mp != (mblk_t *) seq; mp = mp->b_next);
	usual(mp);
	return (mp);
}

dp_t *ss7_list;
STATIC dp_t *t_tok_check(ulong acceptor)
{
	dp_t *ap;
	queue_t *aq = (queue_t *) acceptor;
	for (ap = ss7_list; ap && ap->rq != aq; ap = ap->next);
	usual(ap);
	return (ap);
}
STATIC int t_conn_res(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err = 0;
	mblk_t *cp;
	dp_t *ap;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_conn_res *p = (typeof(p)) mp->b_rptr;
	if (dp->i_state != TS_WRES_CIND)
		goto toutstate;
	dp->i_state = TS_WACK_CRES;
	if (mlen < sizeof(*p))
		goto einval;
	if (mlen < p->OPT_offset + p->OPT_length)
		goto tbadopt;
	if (!(cp = t_seq_check(q, p->SEQ_number)))
		goto tbadseq;
	if (!(ap = t_tok_check(p->ACCEPTOR_id))
	    || (ap != dp && ((1 << ap->i_state) & ~(TSF_UNBND | TSF_IDLE))))
		goto tbadseq;
	if (ap->i_state == TS_IDLE && ap->conind)
		goto tresqlen;
	/* protect at least r00t streams from users */
	if (dp->cred.cr_uid 1 = 0 && ap->cred.cr_uid == 0)
		goto tacces;
	{
		ss7_opts_t opt = { 0, };
		unsigned char *opt_ptr = mp->b_rptr + p->OPT_offset;
		size_t opt_len = p->OPT_length;
		if ((err = ss7_parse_opts(&ops, opt_ptr, opt_len)))
			goto error;
		ap->i_state = TS_DATA_XFER;
		if ((err = dp->pcb.conn_res(q, cp, aq, mp->b_cont))) {
			ap->i_state = ap_oldstate;
			goto error;
		}
		if ((err = t_ok_ack(q, T_CONN_RES, (ulong) cp, (ulong) ap)))
			return (err);
		return (QR_TRIMMED);
	}
      tacces:
	err = TACCES;
	ptrace(("ERROR: no access to accepting queue\n"));
	goto error;
      tresqlen:
	err = TRESQLEN;
	ptrace(("ERROR: accepting queue is listening\n"));
	goto error;
      tbadf:
	err = TBADF;
	ptrace(("ERROR: accepting queue id is invalid\n"));
	goto error;
      tbadseq:
	err = TBADSEQ;
	ptrace(("ERROR: connection ind referenced is invalid\n"));
	goto error;
      tbadopt:
	err = TBADOPT;
	ptrace(("ERROR: options are bad\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      toutstate:
	err = TOUTSTATE;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:
	return t_error_ack(q, T_CONN_RES, err);
}

/* 
 *  T_DISCON_REQ        2 - TC disconnection request
 *  -----------------------------------------------------------------
 */
STATIC int t_discon_req(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	mblk_t *cp = NULL;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_discon_req *p = (typeof(p)) mp->b_rptr;
	if ((1 << dp->i_state) & ~(TSF_WCON_CREQ | TSF_WRES_CIND |
				   TSF_DATA_XFER | TSF_WIND_ORDREL | TSF_WREQ_ORDREL))
		goto toutstate;
	switch (dp->i_state) {
	case TS_WCON_CREQ:
		dp->i_state = TS_WACK_DREQ6;
		break;
	case TS_WRES_CIND:
		dp->i_state = TS_WACK_DREQ7;
		break;
	case TS_DATA_XFER:
		dp->i_state = TS_WACK_DREQ9;
		break;
	case TS_WIND_ORDREL:
		dp->i_state = TS_WACK_DREQ10;
		break;
	case TS_WREQ_ORDREL:
		dp->i_state = TS_WACK_DREQ11;
		break;
	}
	if (mlen < sizeof(*p))
		goto einval;
	if (dp->i_state == TS_WACK_DREQ7 && !(cp = t_seq_check(q, p->SEQ_number)))
		goto tbadseq;
	if ((err = dp->pcb.discon_req(q, cp)))
		goto error;
	return t_ok_ack(q, T_DISCON_REQ, p->SEQ_number, 0);
      tbadseq:
	seldom();
	err = TBADSEQ;
	break;			/* connection ind reference is invalid */
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      toutstate:
	err = TOUTSTATE;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:
	return t_error_ack(q, T_DISCON_REQ, err);
}

/* 
 *  T_DATA_REQ          3 - Connection-Mode data transfer request
 *  -----------------------------------------------------------------
 */
STATIC int t_error_reply(queue_t * q, 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 = ua_allocb(q, 2, BPRI_HI))) {
		mp->b_datap->db_type = M_ERROR;
		*(mp->b_wptr)++ = err < 0 ? -err : err;
		*(mp->b_wptr)++ = err < 0 ? -err : err;
		qreply(q, mp);
		return (0);
	}
	seldom();
	return (-ENOBUFS);
}
STATIC int t_write(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	size_t dlen = mp ? msgdsize(mp) : 0;
	if (dlen == 0 || dlen > dp->mtu)
		goto eproto2;
	if (dp->i_state == TS_IDLE)
		goto discard;
	if ((1 << dp->i_state) & ~(TSF_DATA_XFER | TSF_WREQ_ORDREL))
		goto eproto;
	if ((err = dp->pcb.data_req(q, 0, NULL, mp)))
		goto error;
	return (QR_ABSORBED);	/* absorbed */
      eproto2:
	err = -EPROTO;
	ptrace(("ERROR: bad data size\n"));
	goto error;
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      discard:
	ptrace(("INFO: discarding data in idle state\n"));
	return (0);		/* ignore in idle state */
      error:
	return t_error_reply(q, err);
}
STATIC int t_data_req(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	size_t dlen = mp->b_cont ? msgdsize(mp->b_cont) : 0;
	const struct T_data_req *p = (typeof(p)) mp->b_rptr;
	if (dlen == 0 || dlen > dp->mtu)
		goto eproto2;
	if (dp->i_state == TS_IDLE)
		goto discard;
	if (mlen < sizeof(*p))
		goto einval;
	if ((1 << dp->i_state) & ~(TSF_DATA_XFER | TSF_WREQ_ORDREL))
		goto eproto;
	if ((err = dp->pcb.data_req(q, p->MORE_flag, NULL, mp->b_cont)))
		goto error;
	return (QR_TRIMMED);
      eproto2:
	err = -EPROTO;
	ptrace(("ERROR: bad data size\n"));
	goto error;
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      discard:
	ptrace(("INFO: discarding data in idle state\n"));
	return (0);		/* ignore in idle state */
      error:
	return t_error_reply(q, err);
}

/* 
 *  T_EXDATA_REQ         4 - Expedited data request
 *  -----------------------------------------------------------------
 */
STATIC int t_exdata_req(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	size_t dlen = mp->b_cont ? msgdsize(mp->b_cont) : 0;
	const struct T_exdata_req *p = (struct T_exdata_req *) mp->b_rptr;
	if (dlen == 0 || dlen > dp->esdu)
		goto eproto2;
	if (dp->i_state == TS_IDLE)
		goto discard;
	if (mlen < sizeof(*p))
		goto einval;
	if ((1 << dp->i_state) & ~(TSF_DATA_XFER | TSF_WREQ_ORDREL))
		goto eproto;
	if ((err = dp->pcb.data_req(q, p->MORE_flag, NULL, mp->b_cont)))
		goto error;
	mp->b_cont = NULL;	/* absorbed mp->b_cont */
	return (0);
      eproto2:
	err = -EPROTO;
	ptrace(("ERROR: bad data size\n"));
	goto error;
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      discard:
	ptrace(("INFO: discarding data in idle state\n"));
	return (0);
      error:
	return t_error_reply(q, err);
}

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

/* 
 *  T_BIND_REQ           6 - Bind a TS user to a transport address
 *  -----------------------------------------------------------------
 */
STATIC int t_bind_req(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	struct sockaddr_ss7 *add;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_bind_req *p = (typeof(p)) mp->b_rptr;
	const size_t alen = sizeof(*add);
	if (dp->i_state != TS_UNBND)
		goto toutstate;
	dp->i_state = TS_WACK_BREQ;
	if (mlen < sizeof(*p))
		goto einval;
	add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
	alen = sizeof(*add);
	if ((mlen < p->ADDR_offset + p->ADDR_length) || (p->ADDR_length != alen))
		goto tbadaddr;
	switch (add->sa_family) {
	case AF_SS7:
	case AF_MTP:
	case AF_SCCP:
	case AF_TCAP:
		break;
	default:
		goto tbadaddr2;
	}
	/* we don't allow wildcards just yet */
	if (!add->protoid)
		goto noaddr;
	if (dp->cred.cr_uid != 0 && !add->ssn)
		goto tacces;
	if ((err = ss7_bind_req(q, add, p->CONIND_number)))
		goto error;
	return t_bind_ack(q, &dp->src);
      tacces:
	err = TACCES;
	ptrace(("ERROR: no permission for address\n"));
	goto error;
      tnoaddr:
	err = TNOADDR;
	ptrace(("ERROR: couldn't allocate address\n"));
	goto error;
      tbadaddr2:
	err = TBADADDR;
	ptrace(("ERROR: address is invalid (bad address family)\n"));
	goto error;
      tbadaddr:
	err = TBADADDR;
	ptrace(("ERROR: address is invalid\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      toutstate:
	err = TOUTSTATE;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:
	return t_error_ack(q, T_BIND_REQ, err);
}

/* 
 *  T_UNBIND_REQ         7 - Unbind TS user from transport address
 *  -----------------------------------------------------------------
 */
STATIC int t_unbind_req(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	const struct T_unbind_req *p = (typeof(p)) mp->b_rptr;
	(void) p;
	if (dp->i_state != TS_IDLE)
		goto toutstate;
	dp->i_state = TS_WACK_UREQ;
	if ((err = ss7_unbind_req(q)))
		goto error;
	return t_ok_ack(q, T_UNBIND_REQ, 0, 0);
      toutstate:
	err = TOUTSTATE;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:
	return t_error_ack(q, T_UNBIND_REQ, err);
}

/* 
 *  T_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
 *  SCCP.  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(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_optmgmt_req *p = (typeof(p)) mp->b_rptr;
#ifdef TS_WACK_OPTREQ
	if (dp->i_state == TS_IDLE)
		dp->i_state = TS_WACK_OPTREQ;
#endif
	if (mlen < sizeof(*p))
		goto einval;
	if (mlen < p->OPT_offset + p->OPT_length)
		goto tbadopt;
	{
		ulong flags = p->MGMT_flags;
		size_t opt_len = p->OPT_length;
		unsigned char *opt_ptr = mp->b_rptr + p->OPT_offset;
		struct ss7_opts ops = { 0L, NULL, };
		struct ss7_opts *opsp = NULL;
		if (opt_len)
			goto tbadopt2;
		if ((err = ss7_parse_opts(&ops, opt_ptr, opt_len)))
			goto parse;
		opsp = &ops;
		switch (flags) {
		case T_CHECK:
			ss7_check_opts(dp, opsp);
			return t_optmgmt_ack(q, flags, opsp);
		case T_NEGOTIATE:
			ss7_negotiate_opts(dp, opsp);
			return t_optmgmt_ack(q, flags, opsp);
		case T_DEFAULT:
			/* return defaults for the specified options */
		case T_CURRENT:
			return t_optmgmt_ack(q, flags, opsp);
		}
		goto tbadflag;
	}
      tbadflag:err = TBADFLAG;
	ptrace(("ERROR: bad options flags\n"));
	goto error;
      parse:err = err;
	ptrace(("ERROR: option parsing error\n"));
	goto error;
      tbadopt2:err = TBADOPT;
	ptrace(("ERROR: invalid options\n"));
	goto error;
      tbadopt:err = TBADOPT;
	ptrace(("ERROR: invalid options\n"));
	goto error;
      einval:err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      error:return t_error_ack(q, T_OPTMGMT_REQ, err);
}

/* 
 *  T_ORDREL_REQ        10 - TS user is finished sending
 *  -----------------------------------------------------------------
 */
STATIC int t_ordrel_req(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_ordrel_req *p = (typeof(p)) mp->b_rptr;
	(void) p;
	(void) mlen;
	if (!dp->pcb.ordrel_req)
		goto tnotsupport;
	if ((1 << dp->i_state) & ~(TSF_DATA_XFER | TSF_WREQ_ORDREL))
		goto eproto;
	switch (dp->i_state) {
	case TS_DATA_XFER:
		dp->i_state = TS_WIND_ORDREL;
		break;
	case TS_WREQ_ORDREL:
		dp->i_state = TS_IDLE;
		break;
	}
	if ((err = (*dp->pcb.ordrel_req) (q)) < 0)
		goto error;
	if (err > 0)
		goto error_ack;
	return (QR_DONE);
      tnotsupport:err = TNOTSUPPORT;
	ptrace(("ERROR: primitive not supported by provider\n"));
	goto error_ack;
      eproto:err = -EPROTO;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      error:return t_error_reply(q, err);
      error_ack:return t_error_ack(q, T_ORDREL_REQ, TNOTSUPPORT);
}

/* 
 *  T_OPTDATA_REQ       24- data with options request
 *  -----------------------------------------------------------------
 */
STATIC int t_optdata_req(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	size_t dlen = mp->b_cont ? msgdsize(mp->b_cont) : 0;
	const struct T_optdata_req *p = (typeof(p)) mp->b_rptr;
	if (dlen == 0)
		goto eproto2;
	if (dp->i_state == TS_IDLE)
		goto discard;
	if (mlen < sizeof(*p) || (p->OPT_length && (mlen < p->OPT_offset + p->OPT_length)))
		goto einval;
	if ((1 << dp->i_state) & ~(TSF_DATA_XFER | TSF_WREQ_ORDREL))
		goto eproto;
	{
		ulong more = (p->DATA_flag & T_ODF_MORE);
		ss7_opts_t opt = { 0, };
		if (p->OPT_length) {
			unsigned char *opt_ptr = mp->b_rptr + p->OPT_offset;
			size_t opt_len = p->OPT_length;
			if ((err = ss7_parse_opts(&opt, opt_ptr, opt_len)))
				goto error;
		}
		if (p->DATA_flag & T_ODF_EX) {
			if (!dp->pcb.exdata_req)
				goto tnotsupport;
			if (dlen > dp->esdu)
				goto eproto2;
			if ((err = dp->pcb.exdata_req(q, more, &opt, mp->b_cont)))
				goto error;
		} else {
			if (dlen > dp->sdu)
				goto eproto2;
			if ((err = dp->pcb.data_req(q, more, &opt, mp->b_cont)))
				goto error;
		}
		return (QR_TRIMMED);
	}
      eproto2:err = -EPROTO;
	ptrace(("ERROR: bad data size\n"));
	goto error;
      tnotsupport:err = -EPROTO;
	ptrace(("ERROR: expedited data transfer not supported\n"));
	goto error;
      eproto:err = -EPROTO;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      einval:err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      discard:err = 0;
	ptrace(("INFO: discarding data in idle state\n"));
	goto error;
      error:return t_error_reply(q, err);
}

/* 
 *  T_ADDR_REQ          25 - address request
 *  -----------------------------------------------------------------
 */
STATIC int t_addr_req(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	(void) mp;
	switch (dp->i_state) {
	case TS_UNBND:
	case TS_WACK_BREQ:
		return t_addr_ack(q, NULL, NULL);
	case TS_WACK_UREQ:
	case TS_IDLE:
		return t_addr_ack(q, &dp->src, NULL);
	}
	return t_error_ack(q, T_ADDR_REQ, TOUTSTATE);
}

/* 
 *  T_UNITDATA_REQ      ?? - Unitdata request
 *  -----------------------------------------------------------------
 */
STATIC int m_error_reply(queue_t * q, int error)
{
	dp_t *dp = UA_PRIV(q);
	mblk_t *mp;
	switch (error) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (error);
	case 0:
	case 1:
	case 2:
		never();
		return (error);
	}
	if ((mp = ua_allocb(q, 2, BPRI_HI))) {
		mp->b_datap->db_type = M_ERROR;
		*(mp->b_wptr)++ = error < 0 ? -error : error;
		*(mp->b_wptr)++ = error < 0 ? -error : error;
		qreply(q, mp);
		return (0);
	}
	seldom();
	return (-ENOBUFS);
}
STATIC INLINE int t_unitdata_req(queue_t * q, mblk_t * mp)
{
	dp_t *dp = UA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	size_t dlen = mp->b_cont ? msgdsize(mp->b_cont) : 0;
	const struct T_unitdata_req *p = (typeof(p)) mp->b_rptr;
	struct sockaddr_ss7 *dst;
	if (dlen == 0 || dlen > dp->mtu)
		goto eproto;
	if (dp->i_state != TS_IDLE)
		goto eproto2;
	if (mlen < sizeof(*p))
		goto eproto3;
	dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
	if ((mlen < p->DEST_offset + p->DEST_length) || (p->DEST_length != sizeof(*dst)))
		goto eproto4;
	switch (dst->sa_family) {
	case AF_SS7:		/* signalling data link identifier */
	case AF_MTP:		/* point code */
	case AF_SCCP:		/* global title */
	case AF_TCAP:		/* application context */
		break;
	default:
		goto eproto6;
	}
	if (dp->cred.cr_uid != 0 && (dst->protoid != dp->src.protoid || !dst->protoid))
		goto eproto5;
	{
		ss7_opts_t opt = { 0, };
		unsigned char *opt_ptr = mp->b_rptr + p->OPT_offset;
		size_t opt_len = p->OPT_length;
		if ((err = ss7_parse_opts(&opt, opt_ptr, opt_len)))
			goto error;
		if ((err = ss7_unitdata_req(q, dst, &opt, mp->b_cont)))
			goto error;
		return (QR_TRIMMED);
	}
      eproto6:err = -EPROTO;
	ptrace(("ERROR: Invalid address family\n"));
	goto error;
      eproto5:err = -EPROTO;
	ptrace(("ERROR: no permission to send to address\n"));
	goto error;
      eproto4:err = -EPROTO;
	ptrace(("ERROR: bad destination address\n"));
	goto error;
      eproto3:err = -EPROTO;
	ptrace(("ERROR: invalid primitive\n"));
	goto error;
      eproto2:err = -EPROTO;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      eproto:err = -EPROTO;
	ptrace(("ERROR: bad data size\n"));
	goto error;
      error:return m_error_reply(q, err);
}

/* 
 *  Other primitives    XX - other invalid primitives
 *  -----------------------------------------------------------------
 */
STATIC int t_other_req(queue_t * q, mblk_t * mp)
{
	ulong prim = *((ulong *) mp->b_rptr);
	return t_error_ack(q, prim, TNOTSUPPORT);
}

/* 
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int t_w_ioctl(queue_t * q, mblk_t * mp)
{
	int ret = -EINVAL;
	// void *arg = mp->b_cont?mp->b_cont->b_rptr:NULL;
	struct iocblk *iocp = (typeof(iocp)) mp->b_wptr;
	int cmd = iocp->ioc_cmd;
	switch (_IOC_TYPE(cmd) >> 8) {
	case __SID:
		ret = -EOPNOTSUPP;
		break;
	case SCCP_IOC_MAGIC:
		if (iocp->ioc_count >= _IOC_SIZE(cmd)) {
			int nr = _IOC_NR(cmd);
			switch (nr) {
			default:
				ret = -EOPNOTSUPP;
				break;
			}
		}
		break;
	default:
		ret = -EOPNOTSUPP;
		break;
	}
	mp->b_datap->db_type = ret < 0 ? M_IOCNAK : M_IOCACK;
	iocp->ioc_error = ret < 0 ? -ret : 0;
	iocp->ioc_rval = ret < 0 ? -1 : ret;
	qreply(q, mp);
	return (0);
}

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

/* 
 *  =========================================================================
 *
 *  QUEUE PUT and SERVICE routines
 *
 *  =========================================================================
 *
 *  WRITE PUT and SERVICE (Message from above SCCP-User --> SCCP-Provider)
 *  -------------------------------------------------------------------------
 */
int t_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_PROTO:
	case M_PCPROTO:
		return t_w_proto(q, mp);
	case M_FLUSH:
		return t_w_flush(q, mp);
	case M_IOCTL:
		return t_w_ioctl(q, mp);
	}
	return (-EOPNOTSUPP);
}

/* 
 *  READ PUT and SERVICE (Message from below SCCP-Provider --> SCCP-User)
 *  -------------------------------------------------------------------------
 *  The upper read queue is just a holding area for transferring messages to
 *  the user.  When we process a message we just pass it along.
 */
int t_r_prim(queue_t * q, mblk_t * mp)
{
	(void) q;
	(void) mp;
	return (QR_PASSFLOW);
}


Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/sigtran/ua_upper_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: