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/m2tp/m2tp.c


File /code/strss7/drivers/m2tp/m2tp.c



#ident "@(#) $RCSfile: m2tp.c,v $ $Name:  $($Revision: 0.8.2.2 $) $Date: 2003/04/14 12:12:52 $"

static char const ident[] =
    "$RCSfile: m2tp.c,v $ $Name:  $($Revision: 0.8.2.2 $) $Date: 2003/04/14 12:12:52 $";

/*
 *  This is a M2TP/SCTP driver.  This simulates one or more SS7 links using an
 *  SCTP association.  It is similar to the M2TP/UDP driver except that it has
 *  different path characteristics.
 */

#define M2TP_DESCRIP	"M2TP/SCTP STREAMS MODULE."
#define M2TP_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  All Rights Reserved."
#define M2TP_DEVICES	"Part of the OpenSS7 Stack for LiS STREAMS."
#define M2TP_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define M2TP_LICENSE	"GPL"
#define M2TP_BANNER	M2TP_DESCRIP	"\n" \
			M2TP_COPYRIGHT	"\n" \
			M2TP_DEVICES	"\n" \
			M2TP_CONTACT	"\n"

#ifdef MODULE
MODULE_AUTHOR(M2TP_CONTACT);
MODULE_DESCRIPTION(M2TP_DESCRIP);
MODULE_SUPPORTED_DEVICE(M2TP_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(M2TP_LICENSE);
#endif
#define MODULE_STATIC static
#else
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#define MODULE_STATIC
#endif

#ifdef M2TP_DEBUG
static int m2tp_debug = M2TP_DEBUG;
#else
static int m2tp_debug = 2;
#endif

#ifndef M2TP_CMAJOR
#define M2TP_CMAJOR 240
#endif
#define M2TP_NMINOR 255

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

typedef void (*bufcall_fnc_t) (long);

/*
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */

static struct module_info m2tp_info = {
	0,				/* Module ID number *//* FIXME */
	"m2tp",				/* Module name */
	1,				/* Min packet size accepted */
	512,				/* Max packet size accepted */
	8 * 512,			/* Hi water mark */
	1 * 512				/* Lo water mark */
};

static INT m2tp_rput(queue_t *, mblk_t *);
static INT m2tp_rsrv(queue_t *);
static int m2tp_open(queue_t *, dev_t *, int, int, cred_t *);
static int m2tp_close(queue_t *, int, cred_t *);

static struct qinit m2tp_rinit = {
	m2tp_rput,			/* Read put (msg from below) */
	m2tp_rsrv,			/* Read queue service */
	m2tp_open,			/* Each open */
	m2tp_close,			/* Last close */
	NULL,				/* Admin (not used) */
	&m2tp_info,			/* Information */
	NULL				/* Statistics */
};

static INT m2tp_wput(queue_t *, mblk_t *);
static INT m2tp_wsrv(queue_t *);

static struct qinit m2tp_winit = {
	m2tp_wput,			/* Write put (msg from above) */
	m2tp_wsrv,			/* Write queue service */
	NULL,				/* Each open */
	NULL,				/* Last close */
	NULL,				/* Admin (not used) */
	&m2tp_info,			/* Information */
	NULL				/* Statistics */
};

MODULE_STATIC struct streamtab m2tp_info = {
	&m2tp_rinit,			/* Upper read queue */
	&m2tp_winit,			/* Upper write queue */
	NULL,				/* Lower read queue */
	NULL				/* Lower write queue */
};

/*
 *  =========================================================================
 *
 *  M2TP Private Structure
 *
 *  =========================================================================
 */

typedef struct m2tp m2tp_t;

struct m2tp {
	struct m2tp *next;
	queue_t *rq;			/* STREAM read queue */
	queue_t *wq;			/* STREAM write queue */
	uint state;			/* M2TP state */
	uint t_state;			/* T-Provider state */
	sdt_state_t sdt_state;		/* Signalling Link State */

	sdt_info_ack_t info;

	struct {
		int state;
		int prim;
		int err;
		int reason;
	} d;
} m2tp_t;

#define M2TP_STATUS_IN_SERVICE		(__constant_htonl(1))
#define	M2TP_STATUS_PROCESSOR_OUTAGE	(__constant_htonl(2))
#define M2TP_STATUS_PROCESSOR_ENDED	(__constant_htonl(3))
#define M2TP_STATUS_BUSY		(__constant_htonl(4))
#define M2TP_STATUS_BUSY_ENDED		(__constant_htnol(5))

/*
 *  =========================================================================
 *
 *  M2TP-Provider --> SDT-User Primitives (M_CTL, M_PROTO, M_PCPROTO)
 *
 *  =========================================================================
 */
static inline int sdt_info_ack(m2tp_t * mt)
{
	mblk_t *mp;
	sdt_info_ack_t *p;
	if ((mp = allocb(sizeof(*p)), BPRI_HI)) {
		mp->b_datap->db_type = M_PCPROTO;
		p = (sdt_info_ack_t *) mp->b_wptr;
		*p = mt->info;
		mp->b_wptr += sizeof(*p);
		putnext(mt->rq, mp);
		return (0);
	}
	return -ENOBUFS;
}
static inline int sdt_ok_ack(m2tp_t * mt)
{
	mblk_t *mp;
	sdt_ok_ack_t *p;
	if ((mp = allocb(sizeof(*p)), BPRI_HI)) {
		mp->b_datap->db_type = M_PCPROTO;
		p = (sdt_ok_ack_t *) mp->b_wptr;
		p->sdt_primitive = SDT_OK_ACK;
		p->sdt_correct_primitive = mt->d.prim;
		p->sdt_state = mt->d.state;
		mp->b_wptr += sizeof(*p);
		putnext(mt->rq, mp);
		return (0);
	}
	return -ENOBUFS;
}
static inline int sdt_error_ack(m2tp_t * mt)
{
	mblk_t *mp;
	sdt_error_ack_t *p;
	if ((mp = allocb(sizeof(*p)), BPRI_HI)) {
		mp->b_datap->db_type = M_PCPROTO;
		p = (sdt_error_ack_t *) mp->b_wptr;
		p->sdt_primitive = SDT_ERROR_ACK;
		p->sdt_errno = mt->d.err;
		p->sdt_reason = mt->d.reason;
		p->sdt_error_primitive = mt->d.prim;
		p->sdt_state = mt->d.state;
		mp->b_wptr += sizeof(*p);
		putnext(mt->rq, mp);
		return (0);
	}
	return -ENOBUFS;
}
static inline int sdt_optmgmt_ack(m2tp_t * mt, caddr_t opt_ptr, size_t opt_len)
{
	mblk_t *mp;
	sdt_optmgmt_ack_t *p;
	if ((mp = allocb(sizeof(*p) + opt_len), BPRI_HI)) {
		mp->b_datap->db_type = M_PCPROTO;
		p = (sdt_optmgmt_ack_t *) mp->b_wptr;
		p->sdt_primitive = SDT_ERROR_ACK;
		p->sdt_opt_offset = sizeof(*p);
		p->sdt_opt_length = opt_len;
		p->sdt_mgmt_flags = mt->opt_flags;
		mp->b_wptr += sizeof(*p);
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
		putnext(mt->rq, mp);
		return (0);
	}
	return -ENOBUFS;
}

static inline int sdt_uprim(m2tp_t * mt, int type, int prim, mblk_t * dp)
{
	mblk_t *mp;
	if ((mp = allocb(sizeof(long)), BPRI_HI)) {
		mp->b_datap->db_type = type;
		*((long) mp->b_wptr)++ = prim;
		mp->b_cont = dp;
		putnext(mt->rq, mp);
		return (0);
	}
	return -ENOBUFS;
}
static inline int sdt_event_ind(m2tp_t * mt)
{
	return sdt_uprim(m2, M_PROTO, SDT_EVENT_IND, NULL);
}
static inline int sdt_rc_signal_unit_ind(m2tp_t * mt, mblk_t * dp)
{
	return sdt_uprim(m2, M_PROTO, SDT_RC_SIGNAL_UNIT_IND, dp);
}
static inline int sdt_rc_congestion_accept_ind(m2tp_t * mt)
{
	return sdt_uprim(m2, M_PCPROTO, SDT_RC_CONGESTION_ACCEPT_IND, NULL);
}
static inline int sdt_rc_congestion_discard_ind(m2tp_t * mt)
{
	return sdt_uprim(m2, M_PCPROTO, SDT_RC_CONGESTION_DISCARD_IND, NULL);
}
static inline int sdt_rc_no_congestion_ind(m2tp_t * mt)
{
	return sdt_uprim(m2, M_PCPROTO, SDT_RC_NO_CONGESTION_IND, NULL);
}
static inline int sdt_iac_correct_su_ind(m2tp_t * mt)
{
	return sdt_uprim(m2, M_PROTO, SDT_IAC_CORRECT_SU_IND, NULL);
}
static inline int sdt_iac_abort_proving_ind(m2tp_t * mt)
{
	return sdt_uprim(m2, M_PROTO, SDT_IAC_ABORT_PROVING_IND, NULL);
}
static inline int sdt_lsc_link_failure_ind(m2tp_t * mt)
{
	return sdt_uprim(m2, M_PCPROTO, SDT_LSC_LINK_FAILURE_IND, NULL);
}
static inline int sdt_txc_transmission_request_ind(m2tp_t * mt)
{
	return sdt_uprim(m2, M_PROTO, SDT_TXC_TRANSMISSION_REQUEST_IND, NULL);
}

/*
 *  =========================================================================
 *
 *  M2TP-Provider --> T-Provider (SCTP) Primitives (M_CTL, M_PROTO, M_PCPROTO)
 *
 *  =========================================================================
 */
/*
 *  T_INFO_REQ
 *
 *  We might use this primitive if the T-Provider is not SCTP to check the
 *  capabilities of the T-Provider before exchanging any other primitives.
 */
static int t_info_req(m2tp_t * mt)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  T_BIND_REQ
 *
 *  This is used by M2TP to bind the T-Provider to a local transport address
 *  before attempting to connect the T-Provider.
 */
static int t_bind_req(m2tp_t * mt)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  T_OPTMGMT_REQ
 *
 *  This might be used by M2TP to set some local and associative options
 *  before establishing a connection.
 */
static int t_optmgmt_req(m2tp_t * mt)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  T_ADDR_REQ
 *
 *  This is really not necessary, but it is here anyways.  Particularly if
 *  there is a non-SCTP T-Providder which does not provide the address
 *  information in a usable (or storable) format in the appropriate
 *  primitives.  This should be unnecessary for the OpenSS7 SCTP T-Provider.
 */
static int t_addr_req(m2tp_t * mt)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  T_CONN_REQ
 *
 *  This is used to actively request a connection to the transport peer.
 */
static int t_conn_req(m2tp_t * mt, mblk_t * dp)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  T_CONN_RES
 *
 *  This is used to accept a passive connection from the transport peer.  We
 *  use XTI/TLI semantics here an always accept the connection on the same
 *  stream which was listening.  Remember that SCTP can provide a T_CONN_IND
 *  while in the T_DATAXFER state (for SCTP Restart) which must also be
 *  accepted on this stream.
 */
static int t_conn_res(m2tp_t * mt, mblk_t * dp)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  T_DATA_REQ
 *
 *  This is used to send normal (ordered) messages on the control or default
 *  SCTP-stream (stream 0).
 *
 */
static int t_data_req(m2tp_t * mt, mblk_t * dp)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  T_EXDATA_REQ
 *
 *  This is used to send expedited (really, unordered) messages on the control
 *  or default SCTP-stream (stream 0).
 */
static int t_exdata_req(m2tp_t * mt, mblk_t * dp)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  T_OPTDATA_REQ
 *
 *  This is used to send normal (ordered) or expedited (unordered) data on a
 *  specific stream.
 */
static int t_optdata_req(m2tp_t * mt, mblk_t * dp)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  T_DISCON_REQ
 *
 *  This is used to ABORT the SCTP association under brutal circumstances.
 */
static int t_discon_req(m2tp_t * mt, mblk_t * dp)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  T_ORDREL_REQ
 *
 *  This is used to peform a SHUTDOWN on the SCTP association.
 */
static int t_ordrel_req(m2tp_t * mt, mblk_t * dp)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  T_UNITDATA_REQ
 *
 *  This is not normally used for a SCTP T-Provider (which is *  connection-
 *  oriented only).  However, if we use a connectionless T-Provider we may use
 *  these messages, so here it is.
 */
static int t_unitdata_req(m2tp_t * mt, mblk_t * dp)
{
	ptrace(("Unimplemented T-Provider primitive invoked\n"));
	(void) mt;
	(void) mp;
	return (-EFAULT);
}

/*
 *  Mapping of states:
 *
 *  M2TP State		    T-Provider State(s)
 *  -------------------	    --------------------------------------------
 *  M2TP_STATE_IDLE	    (uninitialized)
 *
 *  M2TP_STATE_OOS	    TS_UNBND TS_WACK_UREQ TS_IDLE TS_WACK_OPREQ
 *			    TS_WACK_DREQ6 TS_WACK_DREQ7 TS_WACK_DREQ9
 *			    TS_WACK_DREQ10 TS_WACK_DREQ11 TS_WIND_ORDREL
 *			    TS_WACK_CREQ TS_WACK_CRES
 *
 *  M2TP_STATE_AIP	    TS_WACK_BREQ TS_IDLE TS_WCON_CREQ TS_WRES_CIND
 *			    TS_WACK_OPREQ TS_WACK_CREQ TS_WACK_CRES
 *
 *  M2TP_STATE_INS_LOCAL    TS_DATA_XFER
 *
 *  M2TP_STATE_INS	    TS_DATA_XFER TS_WREQ_ORDREL
 *
 *  M2TP_RETRIEVAL	    TS_WIND_ORDREL TS_WACK_DREQ6 TS_WACK_DREQ7
 *			    TS_WACK_DREQ9 TS_WACK_DREQ10 TS_WACK_DREQ11
 *			    TS_WACK_CREQ TS_WACK_CRES TS_IDLE TS_WACK_UREQ
 *			    TS_UNBND
 *  -------------------	    --------------------------------------------
 */

/*
 *  =========================================================================
 *
 *  M2TP-Provider --> M2TP-Peer Primitives (Sent Messages)
 *
 *  =========================================================================
 */
static inline int m2tp_send_data(m2tp_t * mt, mblk_t * dp)
{
	mblk_t *mp;
	struct T_data_req *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = (T_ata_req_t *) mp->b_wptr;
		p->PRIM_type = T_OPTDATA_REQ;
		p->MORE_flag = 0;
		mp->b_wptr += sizeof(*p);
		mp->b_cont = dp;
		putnext(mt->wq, mp);
		return (0);
	}
	return -ENOBUFS;
}

/*
 *  =========================================================================
 *
 *  M2TP-Peer --> M2TP-Provider Primitives (Received Messages)
 *
 *  =========================================================================
 */
static inline int m2tp_recv_data(m2tp_t * mt, mblk_t * dp)
{
	return sdt_rc_signal_unit_ind(m2, dp);
}

/*
 *  =========================================================================
 *
 *  SDT-User --> M2TP-Provider Primitives (M_CTL, M_PROTO, M_PCPROTO)
 *
 *  =========================================================================
 *
 *  SDT_INFO_REQ:
 *  -------------------------------------------------------------------------
 */
static int sdt_info_req(m2tp_t * mt, mblk_t * mp)
{
	lmi_info_req_t *p = (lmi_info_req_t *) mp->b_wptr;

	ensure(p->lmi_primitive == LMI_INFO_REQ, return -EFAULT);

	if (sdt_info_ack(mt) == -ENOBUFS)
		return -EAGAIN;

	freemsg(mp);
	return (0);
}

/*
 *  SDT_ATTACH_REQ:
 *  -------------------------------------------------------------------------
 *  This is the attach request.  We take the PPA address and use it directly
 *  as an address to the T_BIND_REQ.
 *
 */
static int sdt_attach_req(m2tp_t * mt, mblk_t * mp)
{
	lmi_attach_req_t *p = (lmi_attach_req_t *) mp->b_rptr;
	caddr_t ppa_ptr = mp->b_rptr + p->ppa_offset;
	size_t ppa_len = p->ppa_length;

	ensure(p->lmi_primitive == LMI_ATTACH_REQ, return -EFAULT);

	if (mt->d.state != LMI_UNATTACHED || mt->t_state != TS_UNBND) {
		mt->d.err = LMI_OUTSTATE;
		if (sdt_error_ack(mt) == -ENOBUFS)
			return -EAGAIN;
		return (0);
	}
	if (t_bind_req(mt, ppa_ptr, ppa_len) == -ENOBUFS)
		return -EAGAIN;

	mt->t_state = TS_WACK_BREQ;
	freemsg(mp);
	return (0);
}

/*
 *  SDT_DETACH_REQ:
 *  -------------------------------------------------------------------------
 *  This is the detach request.  We can now unbind the T-provider.
 */
static int sdt_detach_req(m2tp_t * mt, mblk_t * mp)
{
	lmi_detach_req_t *p = (lmi_detach_req_t *) mp->b_wptr;

	ensure(p->lmi_primitive == LMI_DETACH_REQ, return -EFAULT);

	if (mt->d.state != LMI_DISABLED || mt->t_state != TS_IDLE) {
		mt->d.err = LMI_OUTSTATE;
		if (sdt_error_ack(mt) == -ENOBUFS)
			return -EAGAIN;
		return (0);
	}
	if (t_unbind_req(mt) == -ENOBUFS)
		return -EAGAIN;

	mt->t_state = TS_WACK_UREQ;
	freemsg(mp);
	return (0);
}

/*
 *  SDT_ENABLE_REQ:
 *  -------------------------------------------------------------------------
 *
 */
/*
 *  SDT_DISABLE_REQ:
 *  -------------------------------------------------------------------------
 *
 */
/*
 *  SDT_OPTMGMT_REQ:
 *  -------------------------------------------------------------------------
 *
 */
static int sdt_optmgmt_req(m2tp_t * mt, mblk_t * mp)
{
}

/*
 *  SDT_DAEDT_TRANSMISSION_REQ:
 *  -------------------------------------------------------------------------
 *  A transmission request.  Just feed the data blocks down to the transport
 *  provider.
 */
static int sdt_daedt_transmission_req(m2tp_t * mt, mblk_t * mp)
{
	mblk_t *dp;
	if ((dp = mp)->b_datap->db_type != M_DATA) {
		dp = mp->b_cont;
		freeb(mp);
	}
	if (dp)
		putnext(mt->wq, dp);
	return (0);
}

/*
 *  SDT_DAEDT_START_REQ:
 *  -------------------------------------------------------------------------
 *  We ignore these signals for M2TP.  Once the transport connection has been
 *  established by attaching and enabling the Signalling Data Terminal, the
 *  transmitters are already available.  This is only a power-on signal.  For
 *  SCTP we want to set the initial transmit options here.
 */
static int sdt_daedt_start_req(m2tp_t * mt, mblk_t * mp)
{
	static struct {
		struct sctp_opt_hb hb;
		struct sctp_opt_rto rto;
		struct sctp_opt_dest dst;
		struct sctp_opt_sctp sctp;
	} opt = {
		{ {
		T_INET_SCTP, T_SCTP_HB, sizeof(opt.hb), 0}
		, 0,}
		, { {
		T_INET_SCTP, T_SCTP_RTO, sizeof(opt.rto), 0}
		, 0,}
		, { {
		T_INET_SCTP, T_SCTP_DEST, sizeof(opt.dest), 0}
		, 0,}
		, { {
		T_INET_SCTP, T_SCTP_SCTP, sizeof(opt.sctp), 0}
		, 0,}
	};

	mt->err = 0;
	mt->flags &= ~M2TP_TX_FAIL;

	if (t_optmgmt_req(mt, T_NEGOTIATE, &opt, sizeof(opt)) == -ENOBUFS)
		return -EAGAIN;

	if (mt->merr) {
		mt->flags |= M2TP_TX_FAIL;
		return -EFAULT;
	}

	mt->flags |= M2TP_DAEDT_IN_SERVICE;
	freemsg(mp);
	return (0);
}

/*
 *  SDT_DAEDR_START_REQ:
 *  -------------------------------------------------------------------------
 *  This signal tells us when we can start receiving messages.  We will dump
 *  all transport data messages received until this signal is received.  This
 *  is a power-on signal.  For SCTP we want to set the initial receive options
 *  here.
 */
static int sdt_daedr_start_req(m2tp_t * mt, mblk_t * mp)
{
	static struct {
		struct sctp_opt_hb hb;
		struct sctp_opt_rto rto;
		struct sctp_opt_dest dst;
		struct sctp_opt_sctp sctp;
	} opt = {
		{ {
		T_INET_SCTP, T_SCTP_HB, sizeof(opt.hb), 0}
		, 0,}
		, { {
		T_INET_SCTP, T_SCTP_RTO, sizeof(opt.rto), 0}
		, 0,}
		, { {
		T_INET_SCTP, T_SCTP_DEST, sizeof(opt.dest), 0}
		, 0,}
		, { {
		T_INET_SCTP, T_SCTP_SCTP, sizeof(opt.sctp), 0}
		, 0,}
	};

	mt->err = 0;
	mt->flags &= ~M2TP_RX_FAIL;

	if (t_optmgmt_req(mt, T_NEGOTIATE, &opt, sizeof(opt)) == -ENOBUFS)
		return -EAGAIN;

	if (mt->err) {
		mt->flags |= M2TP_RX_FAIL;
		return -EFAULT;
	}

	mt->flags |= M2TP_DAEDR_IN_SERVICE;
	freemsg(mp);
	return (0);
}

/*
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  SCTP OPTIONS used for AERM and SUERM functions.
 *
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  These are the SCTP options which we use when performing Normal alignment
 *  and Emergency alignment.  For both alignments we want to "prove" the SCTP
 *  association, so arrange to generate some heartbeats and set the other
 *  parameters (retrans maxes, rto max, heartbeat interval) so that the
 *  association will fail within the appropriate proving period (normal or
 *  emergency) is the association is unstable.
 */
static struct m2tp_options {
	struct sctp_opt hb hb;
	struct sctp_opt_rto rot;
	struct sctp_opt_dest dst;
	struct sctp_opt_sctp sctp;
} m2tp_norm_options = {
	{ {
	T_INET_SCTP, T_SCTP_HB, sizeof(opt.hb), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_RTO, sizeof(opt.rto), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_DEST, sizeof(opt.dest), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_SCTP, sizeof(opt.sctp), 0}
	, 0,}
}

, m2tp_emer_options = {
	{ {
	T_INET_SCTP, T_SCTP_HB, sizeof(opt.hb), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_RTO, sizeof(opt.rto), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_DEST, sizeof(opt.dest), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_SCTP, sizeof(opt.sctp), 0}
	, 0,}
}

, m2tp_stop_options = {
	{ {
	T_INET_SCTP, T_SCTP_HB, sizeof(opt.hb), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_RTO, sizeof(opt.rto), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_DEST, sizeof(opt.dest), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_SCTP, sizeof(opt.sctp), 0}
	, 0,}
}

, m2tp_init_options = {
	{ {
	T_INET_SCTP, T_SCTP_HB, sizeof(opt.hb), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_RTO, sizeof(opt.rto), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_DEST, sizeof(opt.dest), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_SCTP, sizeof(opt.sctp), 0}
	, 0,}
}

, m2tp_estb_options = {
	{ {
	T_INET_SCTP, T_SCTP_HB, sizeof(opt.hb), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_RTO, sizeof(opt.rto), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_DEST, sizeof(opt.dest), 0}
	, 0,}
	, { {
	T_INET_SCTP, T_SCTP_SCTP, sizeof(opt.sctp), 0}
	, 0,}
};

/*
 *  SDT_AERM_START_REQ:
 *  -------------------------------------------------------------------------
 *  This signal tells us when we can start sending alignment error monitor
 *  signals.  For SCTP we monitor constantly, so there is no need to turn
 *  monitoring on or off, we just need to know what signals to delivery when.
 */
static int sdt_aerm_start_req(m2tp_t * mt, mblk_t * mp)
{
	if (mt->t_state == TS_DATA_XFER)
		if (!(mt->flags & (M2TP_RX_FAIL | M2TP_TX_FAIL))) {
			struct m2tp_options *opt =
			    (m2->flags & M2TP_EMERGENCY) ? &m2tp_emer_options : &m2tp_norm_options;

			mt->oflags |= M2TP_RX_FAIL | M2TP_TX_FAIL;

			if (t_optmgmt_req(mt, T_NEGOTIATE, opt, sizeof(*opt)) == -ENOBUFS)
				return -EAGAIN;

			mt->flags |= M2TP_AERM_IN_SERVICE;
		}
	freemsg(mp);
	return (0);
}

/*
 *  SDT_AERM_STOP_REQ:
 *  -------------------------------------------------------------------------
 *  This signal tells us when we must stop sending alignment error monitor
 *  signals.  For SCTP we monitor constantly, so there is no need to turn
 *  monitoring on or off, we just need to know what signals to deliver when.
 *  For SCTP we may want to turn off heartbeating.
 */
static int sdt_aerm_stop_req(m2tp_t * mt, mblk_t * mp)
{
	if (mt->t_state == TS_DATA_XFER)
		if (!(mt->flags & (M2TP_RX_FAIL | M2TP_TX_FAIL))) {
			struct m2tp_options *opt = &m2tp_stop_options;

			mt->oflags |= M2TP_RX_FAIL | M2TP_TX_FAIL;

			if (t_optmgmt_req(mt, T_NEGOTIATE, opt, sizeof(*opt)) == -ENOBUFS)
				return -EAGAIN;
		}

	m2->flags &= ~M2TP_AERM_IN_SERVICE;
	freemsg(mp);
	return (0);
}

/*
 *  SDT_AERM_SET_TI_TO_TIN_REQ:
 *  -------------------------------------------------------------------------
 *  This signal tells us that we are performing normal alignment procedures.
 *  We may want to adjust some of the T-Provider parameters for error
 *  monitoring or QOS.  For example, for SCTP we may want to adjust the
 *  heartbeat interval, Assoc.Max.Retrans and Path.Max.Retrans and RTO.max for
 *  the longer proving period.
 */
static int sdt_aerm_set_ti_to_tin_req(m2tp_t * mt, mblk_t * mp)
{
	/* we don't change proving parameters in the middle of proving */
	m2->flags &= ~M2TP_EMERGENCY;
	freemsg(mp);
	return (0);
}

/*
 *  SDT_AERM_SET_TI_TO_TIE_REQ:
 *  -------------------------------------------------------------------------
 *  This signal tells us that we are performing emergency alignment
 *  procedures.  We may want to adjust some of the T-Provider parameters for
 *  error monitoring or QOS.  For example, for SCTP we may want to adjust the
 *  heartbeat interval, Assoc.Max.Retrans and Path.Max.Retrans and RTO.max for
 *  the shorter proving period.
 */
static int sdt_aerm_set_ti_to_tie_req(m2tp_t * mt, mblk_t * mp)
{
	/* we don't change proving parameters in the middle of proving */
	m2->flags |= M2TP_EMERGENCY;
	freemsg(mp);
	return (0);
}

/*
 *  SDT_SUERM_START_REQ:
 *  -------------------------------------------------------------------------
 *  This signal tells us when we must start sending error monitor signals.
 *  For reliable transport providers we monitor constantly, so there is no
 *  need to turn monitoring on or off, we just need to know what signals to
 *  deliver when.  For SCTP we may want to adjust the heartbeat interval,
 *  Assoc.Max.Retrans and Path.Max.Retrans and RTO.max for stable error
 *  monitoring.
 */
static int sdt_suerm_start_req(m2tp_t * mt, mblk_t * mp)
{
	if (mt->t_state == TS_DATA_XFER)
		if (!(mt->flags & (M2TP_RX_FAIL | M2TP_TX_FAIL))) {
			struct m2tp_options *opt = &m2tp_estb_options;

			mt->oflags |= M2TP_RX_FAIL | M2TP_TX_FAIL;

			if (t_optmgmt_req(mt, T_NEGOTIATE, &opt, sizeof(opt)) == -ENOBUFS)
				return -EAGAIN;

			m2->flags |= M2TP_SUERM_IN_SERVICE;
		}
	freemsg(mp);
	return (0);
}

/*
 *  SDT_SUERM_STOP_REQ:
 *  -------------------------------------------------------------------------
 *  This signal tells use when we must stop sending error monitor signals.
 *  For reliable transport providers, we monitor constantly, so there is no
 *  need to turn monitoring on or off, we just need to know what signals to
 *  deliver when.  For SCTP we may want to turn off hearbeating.
 */
static int sdt_suerm_stop_req(m2tp_t * mt, mblk_t * mp)
{
	if (mt->t_state == TS_DATA_XFER)
		if (!(mt->flags & (M2TP_RX_FAIL | M2TP_TX_FAIL))) {
			struct m2tp_options *opt = &m2tp_stop_options;

			mt->oflags |= M2TP_RX_FAIL | M2TP_TX_FAIL;

			if (t_optmgmt_req(mt, T_NEGOTIATE, &opt, sizeof(opt)) == -ENOBUFS)
				return -EAGAIN;
		}
	m2->flags &= ~M2TP_SUERM_IN_SERVICE;
	freemsg(mp);
	return (0);
}

/*
 *  =========================================================================
 *
 *  T-Provider (SCTP) --> M2TP-Provider Primitives (M_CTL, M_PROTO, M_PCPROTO)
 *
 *  =========================================================================
 *
 *  T_INFO_ACK
 *
 *  We might need this if we are using other transports and we send a
 *  T_INFO_REQ to the T-Provider to see what semantics we should use for
 *  encapsulating messages and stuff.  If we are only using SCTP, we know what
 *  to expect and will never see this message.
 */
static int t_info_ack(m2tp_t * mt, mblk_t * mp)
{
	struct T_info_ack *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  T_BIND_ACK
 *
 *  SCTP will send us this response when we go to bind the stream for
 *  connection or listening.  We should acknowledge this fact and move to the
 *  next state in attempting to bring up the SCTP association.
 */
static int t_bind_ack(m2tp_t * mt, mblk_t * mp)
{
	struct T_bind_ack *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  T_OPTMGMT_ACK
 *
 *  SCTP will send us this repsonse when we use a T_OPTMGMT_REQ to set the
 *  number of inbound and outbound SCTP-streams.  Actually, we never really
 *  need this message (T_CONN_REQ and T_CONN_RES work just fine) so we should
 *  not expect this message.  This is a T-Provider error.  We should M_ERROR
 *  out the stream.
 */
static int t_optmgmt_ack(m2tp_t * mt, mblk_t * mp)
{
	struct T_optmgmt_ack *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  T_ADDR_ACK
 *
 *  SCTP should no send us this message, because we never send a T_ADDR_REQ to
 *  SCTP, so we do not expect the response.  This is a T-Provider error.  We
 *  should M_ERROR out the queue.
 */
static int t_addr_ack(m2tp_t * mt, mblk_t * mp)
{
	struct T_addr_ack *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  T_OK_ACK
 *
 *  SCTP has acknowledged receipt of our last primitive.  We can move to the
 *  successful state.
 */
static int t_ok_ack(m2tp_t * mt, mblk_t * mp)
{
	struct T_ok_ack *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  T_ERROR_ACK
 *
 *  SCTP has refused our last primitive.  We can either respond with an error
 *  to the M2TP user or we will have to M_ERROR out the stream.
 */
static int t_error_ack(m2tp_t * mt, mblk_t * mp)
{
	struct T_error_ack *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  T_CONN_IND
 *
 *  SCTP has given us a connection indication.  Even though we might not set
 *  the CONIND_number to a positive integer (we set it to zero), we may still
 *  receive a T_CONN_IND if the SCTP association has restarted.  We should
 *  take appropriate action depending on what we think the state of the
 *  association is: if we are waiting to connect we should accept the
 *  connection with a T_CONN_RES with the current stream, or we should refuse
 *  the request with a T_DISCON_REQ.
 */
static int t_conn_ind(m2tp_t * mt, mblk_t * mp)
{
	struct T_conn_ind *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  T_CONN_CON
 *
 *  SCTP has informed us that our connection request was successful.  We
 *  should inform the M2TP user and get ready to transfer data.
 */
static int t_conn_con(m2tp_t * mt, mblk_t * mp)
{
	if (mt->t_state == TS_WCON_CREQ) {
		struct T_conn_con *p = (struct T_conn_con *) mp->b_wptr;

		/* 
		 *  TODO: check for 2 streams inbound only.
		 */
		switch (mt->state) {
		case M2TP_AIP:
			mt->state = M2TP_INS_LOCAL;
			mt->t_state = TS_DATA_XFER;
			/* 
			 *  TODO: Get SRTT report.
			 */
			if (mp->b_cont)
				m2tp_recv_data(mp->b_cont);
			if (srtt <= mt->srtt_max) {
				if (m2tp_send_in_service(mt) == -ENOBUFS)
					return -EAGAIN;
			} else {
				mt->failure_reason = FIXME;
				t_discon_req(mt);
				mt->t_state = TS_WACK_DREQ9;
				sdt_out_of_service(mt);
				mt->state = M2TP_OOS;
			}
			break;
		default:
		case M2TP_IDLE:
		case M2TP_OOS:
		case M2TP_RETRIEVAL:
		case M2TP_INS_LOCAL:
		case M2TP_INS:
			break;
		}
	}
	freemsg(mp);
	return (0);
}

/*
 *  T_DATA_IND
 *
 *  SCTP has given us data on STREAM 0.  This is a control channel for M2TP,
 *  so we should process these messages as control messages.
 */
static int t_data_ind(m2tp_t * mt, mblk_t * mp)
{
	struct T_data_ind *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  T_EXDATA_IND
 *
 *  SCTP has given us out-of-order data on STREAM 0.  This is a control
 *  channel for M2TP so we should process these messages as control messages.
 */
static int t_exdata_ind(m2tp_t * mt, mblk_t * mp)
{
	struct T_exdata_ind *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  T_OPTDATA_IND
 *
 *  SCTP has given us data on a specific SCTP-stream.  We should process this
 *  message for the SCTP-stream indicated.
 */
static int t_optdata_ind(m2tp_t * mt, mblk_t * mp)
{
	struct T_optdata_ind *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  T_OPTDATA_CON (Not in XTI/TLI)
 *
 *  SCTP has given us an indication that an outstanding data message with
 *  given user and provider/peer token values has been acknowledged received
 *  by the peer.  We can strike this message from our retrieval buffers.
 */
static int t_optdata_con(m2tp_t * mt, mblk_t * mp)
{
	struct T_optdata_con *p = (T_optdata_con *) mp->b_rptr;
	mblk_t *dp = (mblk_t *) p->TOKEN_value;
	mblk_t *bp = mt->txq.q_head;

	/* little check just to be sure that it is in the bufq */
	for (; bp && bp != dp; bp = bp->next);
	if (bp != dp)
		return (-EPROTO);
	bufq_unlink(&mt->txq, dp);
	m2tp_recalc_congestion(mt);
	freemsg(mp);
	return (0);
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  T_DISCON_IND
 *
 *  We have received an abortive disconnect.  This is either a response to a
 *  previous T_CONN_REQ during the connection establishment phase or it is
 *  an abort from the data transfer or connection release phases.  Which phase
 *  we are in pretty much determines what we do in response to this
 *  indication.
 */
static int t_discon_ind(m2tp_t * mt, mblk_t * mp)
{
	struct T_discon_ind *p = (struct T_discon_ind *) mp->b_wptr;

	mt->failure_reason = t_to_sl_reason(p->DISCON_reason);
	if (sdt_out_of_service_ind(mt) == -ENOBUFS)
		return -EAGAIN;
	switch (mt->state) {
	case M2TP_STATE_INS:
		mt->state = M2TP_STATE_RETRIEVAL;
		break;
	default:
		mt->state = M2TP_STATE_OOS;
		break;
	}
	mt->t_state = TS_IDLE;
	if (t_unbind_req(mt) == -ENOBUFS)
		return -EAGAIN;
	mt->t_state = TS_WACK_UREQ;
	freemsg(mp);
	return (0);
}

/*
 *  T_ORDREL_IND
 *
 *  We have received an orderly release indication from the peer.  This is
 *  peculiar if we are not expecting it.
 */
static int t_ordrel_ind(m2tp_t * mt, mblk_t * mp)
{
	if (sdt_out_of_service_ind(mt) == -ENOBUFS)
		return -EAGAIN;
	if (t_ordrel_req(mt) == -ENOBUFS)
		return -EAGAIN;
	mt->state = M2TP_STATE_RETRIEVAL;
	mt->t_state = TS_IDLE;
	if (t_unbind_req(mt) == -ENOBUFS)
		return -EAGAIN;
	mt->t_state = TS_WACK_UREQ;
	freemsg(mp);
	return (0);
}

/*
 *  T_UNITDATA_IND
 *
 *  SCTP should not send us this message; however, we might want to use other
 *  transports for M2TP, so we process this message as well as possible.
 */
static int t_unitdata_ind(m2tp_t * mt, mblk_t * mp)
{
	struct T_unitdata_ind *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  SCTP should not send us this message; however, we might want to use other
 *  transports for M2TP, so we process this message as well as possible.
 */
static int t_uderror_ind(m2tp_t * mt, mblk_t * mp)
{
	struct T_uderror_ind *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void) mt;
	(void) mp;
	return (-EPROTO);
}

/*
 *  =========================================================================
 *
 *  M2TP IOCTLs
 *
 *  =========================================================================
 */

/*
 *  =========================================================================
 *
 *  Message Handling
 *
 *  =========================================================================
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 *
 *  M_DATA messages are data messages which are passed between protocol
 *  modules or between protocol modules and users.
 *  
 */
static inline int m2tp_w_data(queue_t * q, mblk_t * mp)
{
	m2tp_t *mt = (m2tp_t *) q->q_ptr;
	return m2tp_send_data(mt, mp);
}
static inline int m2tp_r_data(queue_t * q, mblk_t * mp)
{
	m2tp_t *mt = (m2tp_t *) q->q_ptr;
	return m2tp_recv_data(mt, mp);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO Handling
 *
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO message are normal protocol messages which are passed between
 *  protocol modules or between protocol modules and users.
 */
static inline int m2tp_w_proto(queue_t * q, mblk_t * mp)
{
	m2tp_t *mt = (m2tp_t *) q->q_ptr;
	switch ((mt->dprim = ((union M2TP_primitives *) (mp->b_rptr))->type)) {
	case SDT_INFO_REQ:
		return sdt_info_req(mt, mp);
	case SDT_ATTACH_REQ:
		return sdt_attach_req(mt, mp);
	case SDT_DETACH_REQ:
		return sdt_detach_req(mt, mp);
	case SDT_ENABLE_REQ:
		return sdt_enable_req(mt, mp);
	case SDT_DISABLE_REQ:
		return sdt_disable_req(mt, mp);
	case SDT_OPTMGMT_REQ:
		return sdt_optmgmt_req(mt, mp);
	case SDT_DAEDT_TRANSMISSION_REQ:
		return sdt_daedt_transmission_req(mt, mp);
	case SDT_DAEDT_START_REQ:
		return sdt_daedt_start_req(mt, mp);
	case SDT_DAEDR_START_REQ:
		return sdt_daedr_start_req(mt, mp);
	case SDT_AERM_START_REQ:
		return sdt_aerm_start_req(mt, mp);
	case SDT_AERM_STOP_REQ:
		return sdt_aerm_stop_req(mt, mp);
	case SDT_AERM_SET_TI_TO_TIN_REQ:
		return sdt_aerm_set_ti_to_tin_req(mt, mp);
	case SDT_AERM_SET_TI_TO_TIE_REQ:
		return sdt_aerm_set_ti_to_tie_req(mt, mp);
	case SDT_SUERM_START_REQ:
		return sdt_suerm_start_req(mt, mp);
	case SDT_SUERM_STOP_REQ:
		return sdt_suerm_stop_req(mt, mp);
	}
	return (-EOPNOTSUPP);
}
static inline int m2tp_r_proto(queue_t * q, mblk_t * mp)
{
	m2tp_t *mt = (m2tp_t *) q->q_ptr;
	switch ((mt->uprim = ((union T_primitives *) (mp->b_rptr))->type)) {
	case T_DATA_IND:
		return t_data_ind(mt, mp);
	case T_EXDATA_IND:
		return t_exdata_ind(mt, mp);
	case T_OPTDATA_IND:
		return t_optdata_ind(mt, mp);
	case T_CONN_IND:
		return t_conn_ind(mt, mp);
	case T_CONN_CON:
		return t_conn_con(mt, mp);
	case T_DISCON_IND:
		return t_discon_ind(mt, mp);
	case T_ORDREL_IND:
		return t_ordrel_ind(mt, mp);
	case T_UNITDATA_IND:
		return t_unitdata_ind(mt, mp);
	case T_UDERROR_IND:
		return t_uderror_ind(mt, mp);
	}
	return (-EOPNOTSUPP);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 *
 *  M_PCPROTO messages are priority protocol messages which are passed between
 *  protocol modules or between protocol modules and users.
 */
static inline int m2tp_w_pcproto(queue_t * q, mblk_t * mp)
{
	m2tp_t *mt = (m2tp_t *) q->q_ptr;
	switch ((mt->dprim = ((union M2TP_primitives *) (mp->b_rptr))->type)) {
	case SDT_INFO_REQ:
		return sdt_info_req(mt, mp);
	case SDT_OPTMGMT_REQ:
		return sdt_optmgmt_req(mt, mp);
	case SDT_DAEDT_TRANSMISSION_REQ:
		return sdt_daedt_transmission_req(mt, mp);
	case SDT_DAEDT_START_REQ:
		return sdt_daedt_start_req(mt, mp);
	case SDT_DAEDR_START_REQ:
		return sdt_daedr_start_req(mt, mp);
	case SDT_AERM_START_REQ:
		return sdt_aerm_start_req(mt, mp);
	case SDT_AERM_STOP_REQ:
		return sdt_aerm_stop_req(mt, mp);
	case SDT_AERM_SET_TI_TO_TIN_REQ:
		return sdt_aerm_set_ti_to_tin_req(mt, mp);
	case SDT_AERM_SET_TI_TO_TIE_REQ:
		return sdt_aerm_set_ti_to_tie_req(mt, mp);
	case SDT_SUERM_START_REQ:
		return sdt_suerm_start_req(mt, mp);
	case SDT_SUERM_STOP_REQ:
		return sdt_suerm_stop_req(mt, mp);
	}
	return (-EOPNOTSUPP);

}
static inline int m2tp_r_pcproto(queue_t * q, mblk_t * mp)
{
	m2tp_t *mt = (m2tp_t *) q->q_ptr;
	switch ((mt->uprim = ((union T_primitives *) (mp->b_rptr))->type)) {
	case T_INFO_ACK:
		return t_info_ack(mt, mp);
	case T_OPTMGMT_ACK:
		return t_optmgmt_ack(mt, mp);
	case T_BIND_ACK:
		return t_bind_ack(mt, mp);
	case T_OK_ACK:
		return t_ok_ack(mt, mp);
	case T_ERROR_ACK:
		return t_error_ack(mt, mp);
	case T_ADDR_ACK:
		return t_addr_ack(mt, mp);
		/* new primitives for M2TP support */
	case T_OPTDATA_CON:
		return t_optdata_con(mt, mp);
	}
	return (-EOPNOTSUPP);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_CTL Handling
 *
 *  -------------------------------------------------------------------------
 *
 *  M_CTL messages are used to pass control messages between protocol modules
 *  (not to or from user space).
 */
static inline int m2tp_w_ctl(queue_t * q, mblk_t * mp)
{
	m2tp_t *mt = (m2tp_t *) q->q_ptr;
	switch (((union M2TP_controls *) (mp->b_wptr))->type) {
	case M2TP_CTL_XXXX:
		return m2tp_ctl_xxxx(mt, mp);
	}
	return (-EOPNOTSUPP);
}
static inline int m2tp_r_ctl(queue_t * q, mblk_t * mp)
{
	m2tp_t *mt = (m2tp_t *) q->q_ptr;
	switch (((union M2TP_controls *) (mp->b_wptr))->type) {
	case T_CTL_XXXX:
		return t_ctl_xxxx(mt, mp);
	}
	return (-EOPNOTSUPP);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline int m2tp_do_ioctl(m2tp_t * mt, int cmd, void *arg)
{
	int nr = _IOC_NR(cmd);
	(void) nr;

	if (_IOC_TYPE(cmd) == M2TP_IOC_MAGIC)
		switch (cmd) {
		case M2TP_IOCXXXXX:
			return m2tp_iocxxxx(mt, cmd, arg);
		case M2TP_IOCXXXXX:
			return m2tp_iocxxxx(mt, cmd, arg);
		case M2TP_IOCXXXXX:
			return m2tp_iocxxxx(mt, cmd, arg);
		case M2TP_IOCXXXXX:
			return m2tp_iocxxxx(mt, cmd, arg);
		}
	return -ENXIO;
}
static inline int m2tp_w_ioctl(queue_t * q, mblk_t * mp)
{
	m2tp_t *mt = (m2tp_t *) q->q_ptr;
	struct iocblk *iocp = (struct iocblk *) mp->b_rptr;
	void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL;
	int cmd = iocp->ioc_cmd, count = iocp->ioc_count;
	int ret = -EINVAL;
	struct linkblk *lp = (struct linkblk *) arg;

	switch (cmd) {
	case I_STR:
		if (count >= _IOC_SIZE(cmd)) {
			ret = m2tp_do_ioctl(mt, cmd, arg);
		}
		if (ret == -ENXIO) {
			if (q->q_next) {
				putnext(q, mp);
				return (0);
			}
		}
		break;
	default:
	case I_LINK:
	case I_PLINK:
	case I_UNLINK:
	case I_PUNLINK:
		ret = -EINVAL;
		break;
	}
	if (ret == 0) {
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval = 0;
	} else {
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = -ret;
		iocp->ioc_rval = -1;
	}
	qreply(q, mp);
	return (0);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline void m2tp_w_flush(queue_t * q, mblk_t * mp)
{
	if (*mp->b_rptr & FLUSHW) {
		flushq(q, FLUSHALL);
		if (q->q_next) {
			putnext(q, mp);
			return;
		}
		*mp->b_rptr &= ~FLUSHW;
	}
	if (*mp->b_rptr & FLUSHR) {
		flushq(RD(q), FLUSHALL);
		qreply(q, mp);
		return;
	}
	if (q->q_next) {
		putnext(q, mp);
		return;
	}
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline void m2tp_r_error(queue_t * q, mblk_t * mp)
{
	m2tp_t *mt = (m2tp_t *) q->q_ptr;
	m2tp->state = M2TP_STATE_MERROR;
	if (q->q_next) {
		putnext(q, mp);
		return;
	}
}

/*
 *  =========================================================================
 *
 *  STREAMS QUEUE PUT and QUEUE SERVICE routines
 *
 *  =========================================================================
 *
 *  READ QUEUE PUT and SRV routines
 *
 *  -------------------------------------------------------------------------
 *
 *  M2TP RPUT - Message from below.
 *
 *  If the message is a priority message we attempt to process it immediately.
 *  If the message is a non-priority message, but there are no messages on the
 *  queue yet, we attempt to process it immediately.  If the message is not
 *  supported, we pass it down-queue if possible.  If the message cannot be
 *  processed immediately we place it on the queue.
 */
static INT m2tp_rput(queue_t * q, mblk_t * mp)
{
	int err = -EOPNOTSUPP;

	if (mp->b_datap->db_type < QPCTL && q->q_count) {
		putq(q, mp);
		/* 
		 *  NOTE:- after placing messages on the queue here, I should
		 *  check if placing the message on the queue crosses a band
		 *  threshold for congestion accept and congestion discard.
		 *  When crossing congestion accept, I should sent busy to the
		 *  peer and notify MTP3.  When crossing congestion discard I
		 *  should notify MTP3.
		 */
		return (0);
	}
	switch (mp->b_datap->db_type) {
	case M_DATA:
		if ((err = m2tp_r_data(q, mp)))
			break;
		return (0);
	case M_PROTO:
		if ((err = m2tp_r_proto(q, mp)))
			break;
		return (0);
	case M_PCPROTO:
		if ((err = m2tp_r_pcproto(q, mp)))
			break;
		return (0);
	case M_CTL:
		if ((err = m2tp_r_ctl(q, mp)))
			break;
		return (0);
	case M_ERROR:
		m2tp_r_error(q, mp);
		return (0);
	}
	switch (err) {
	case -EAGAIN:
		putq(q, mp);
		return (0);
	case -EOPNOTSUPP:
		if (q->q_next) {
			putnext(q, mp);
			return (0);
		}
	}
	freemsg(mp);
	return (err);
}

/*
 *  M2TP RSRV - Queued message from below.
 *
 *  If the message is a priority message we attempt to process it immediately
 *  and without flow control.  If the message is a non-priority message and
 *  the next queue is flow controlled, we put the message back on the queue
 *  and wait.  If we cannot process a priority message immediately we cannot
 *  place it back on the queue and discard it.  We requeue non-priority
 *  messages which cannot be processed immediately.  Unrecognized messages are
 *  passed down-queue.
 */
static INT m2tp_rsrv(queue_t * q)
{
	mblk_t *mp;
	int err = -EOPNOTSUPP;

	while ((mp = getq(q))) {
		if (mp->b_datap->db_type < QPCTL && !canputnext(q)) {
			putbq(q, mp);
			return (0);
		}
		switch (mp->b_datap->db_type) {
		case M_DATA:
			if ((err = m2tp_r_data(q, mp)))
				break;
			goto rsrv_continue;
		case M_PROTO:
			if ((err = m2tp_r_proto(q, mp)))
				break;
			goto rsrv_continue;
		case M_PCPROTO:
			if ((err = m2tp_r_pcproto(q, mp)))
				break;
			goto rsrv_continue;
		case M_CTL:
			if ((err = m2tp_r_ctl(q, mp)))
				break;
			goto rsrv_continue;
		}
		switch (err) {
		case -EAGAIN:
			if (mp->b_datap->db_type < QPCTL) {
				putbq(q, mp);
				return (0);
			}
		case -EOPNOTSUPP:
			if (q->q_next) {
				putnext(q, mp);
				goto rsrv_continue;
			}
			break;
		}
		freemsg(mp);
	      rsrv_continue:
		/* 
		 *  NOTE:-  I have removed and processed a message from the
		 *  receive queue, so I should check for receive congestion
		 *  abatement.  If receive congestion has abated below the
		 *  accept threshold, I should signal MTP that there is no
		 *  longer any receive congestion.
		 */
		continue;
	}
	return (0);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  WRITE QUEUE PUT and SRV routines
 *
 *  -------------------------------------------------------------------------
 *
 *  SCTP WPUT - Message from above.
 *
 *  If the message is priority message we attempt to process it immediately.
 *  If the message is non-priority message, but there are no messages on the
 *  queue yet, we attempt to process it immediately.  If the message is not
 *  supported, we pass it down-queue if possible.  If the message cannot be
 *  processed immediately, we place it on the queue.
 */
static INT m2tp_wput(queue_t * q, mblk_t * mp)
{
	mblk_t *mp;
	int err = -EOPNOTSUPP;
	if (q->q_count && mp->b_datap->db_type < QPCTL) {
		putq(q, mp);
		/* 
		 *  NOTE:- after placing messages on the queue here, I should
		 *  check for transmit congestion.  I should check if placing
		 *  the message on the queue crosses a band threshold for
		 *  congestion onset and abatement.  When crossing congestion
		 *  thresholds, I should notify MTP3.
		 */
		return (0);
	}
	switch (mp->b_datap->db_type) {
	case M_DATA:
		if ((err = m2tp_w_data(q, mp)))
			break;
		return (0);
	case M_PROTO:
		if ((err = m2tp_w_proto(q, mp)))
			break;
		return (0);
	case M_PCPROTO:
		if ((err = m2tp_w_pcproto(q, mp)))
			break;
		return (0);
	case M_CTL:
		if ((err = m2tp_w_ctl(q, mp)))
			break;
		return (0);
	case M_IOCTL:
		if ((err = m2tp_w_ioctl(q, mp)))
			break;
		return (0);
	case M_FLUSH:
		m2tp_w_flush(q, mp);
		return (0);
	}
	switch (err) {
	case -EAGAIN:
		if (mp->b_datap->db_type < QPCTL) {
			putq(q, mp);
			return (0);
		}
	case -EOPNOTSUPP:
		if (q->q_next) {
			putnext(q, mp);
			return (0);
		}
	}
	freemsg(mp);
	return (err);
}

/*
 *  M2TP WSRV = Queued message from above.
 *
 *  If the message is a priority message we attempt to process it immediately
 *  and without flow control.  If the message is a non-priority message and
 *  the next queue is flow controlled, we put the message back on the queue
 *  and wait.  If we cannot process a priority message immediately we cannot
 *  place it back on the queue, we discard it.  We requeue non-priority
 *  messages which cannot be processed immediately.  Unrecognized messages are
 *  passed down-queue.
 */
static INT m2tp_wsrv(queue_t * q)
{
	int err = -EOPNOTSUPP;
	mblk_t *mp;

	while ((mp = getq(q))) {
		if (q->q_next && mp->b_datap->db_type < QPCTL && !canputnext(q)) {
			putbq(q, mp);
			return (0);
		}
		switch (mp->b_datap->db_type) {
		case M_DATA:
			if ((err = m2tp_w_data(q, mp)))
				break;
			goto wsrv_continue;
		case M_PROTO:
			if ((err = m2tp_w_proto(q, mp)))
				break;
			goto wsrv_continue;
		case M_PCPROTO:
			if ((err = m2tp_w_pcproto(q, mp)))
				break;
			goto wsrv_continue;
		case M_CTL:
			if ((err = m2tp_w_ctl(q, mp)))
				break;
			goto wsrv_continue;
		}
		switch (err) {
		case -EAGAIN:
			if (mp->b_datap->db_type < QPCTL) {
				putbq(q, mp);
				return (0);
			}
		case -EOPNOTSUPP:
			if (q->q_next) {
				putnext(q, mp);
				goto wsrv_continue;
			}
		}
		freemsg(mp);
	      wsrv_continue:
		/* 
		 *  NOTE:-   I have removed a message from the queue, so I
		 *  should check for band congestion abatement for the data
		 *  band to see if transmit congestion has abated.  If it has,
		 *  I should notify MTP3 of transmit abatement.
		 */
		continue;
	}
	return (0);
}

/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 */
/*
 *  Private structure allocation and deallocation.
 *
 *  We use Linux hardware aligned cache here for speedy access to information
 *  contained in the private data structure.
 */
kmem_cache_t *m2tp_cachep = NULL;

static m2tp_t *m2tp_alloc_priv(queue_t * q)
{
	m2tp_t *m2tp;

	if ((m2tp = kmem_cache_alloc(m2tp_cachep))) {
		bzero(m2tp, sizeof(*m2tp));
		RD(q)->q_ptr = WR(q)->q_tpr = m2tp;
		m2tp->rq = RD(q);
		m2tp->wq = WR(q);
	}
	return (m2tp);
}

static void m2tp_free_priv(queue_t * q)
{
	m2tp_t *m2tp = (m2tp_t *) q->q_ptr;
	kmem_cache_free(m2tp_cachep, m2tp);
	return;
}

static void m2tp_init_priv(void)
{
	if (!(m2tp_cachep = kmem_find_general_cachep(sizeof(m2tp_t)))) {
		/* allocate a new cachep */
	}
	return;
}

/*
 *  -------------------------------------------------------------------------
 *
 *  OPEN - Each open
 *
 *  -------------------------------------------------------------------------
 */
static int m2tp_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	(void) crp;

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

	if (sflag == MODOPEN || WR(q)->q_next != NULL) {
		/* 
		 *  FIXME: check to make sure that the module we are being
		 *  pushed over is compatible (i.e. it is the right kind of
		 *  transport module.
		 */
		if (!(m2tp_alloc_priv(q)))
			return ENOMEM;
		return (0);
	}
	return EIO;
}

/*
 *  -------------------------------------------------------------------------
 *
 *  CLOSE - Last close
 *
 *  -------------------------------------------------------------------------
 */
static int m2tp_close(queue_t * q, int flag, cred_t * crp)
{
	(void) flag;
	(void) crp;

	m2tp_free_priv(q);
	return (0);
}

/*
 *  =========================================================================
 *
 *  LiS MODULE INITIALIZATION
 *
 *  =========================================================================
 */

static int m2tp_initialized = 0;

#ifndef LIS_REGISTERED
static inline void m2tp_init(void)
#else
__initfunc(void m2tp_init(void))
#endif
{
	if (m2tp_initialized)
		return;
	m2tp_initialized = 1;
	printk(KERN_INFO M2TP_BANNER);	/* console splash */
#ifndef LIS_REGISTERED
	if (!(m2tp_minfo.mi_idnum = lis_register_strmod(&m2tp_info, m2tp_minfo.mi_idname))) {
		cmn_err(CE_NOTE, "m2tp: couldn't register as module\n");
		m2tp_minfo.mi_idnum = 0;
	}
#endif
}

#ifndef LIS_REGISTERED
static inline void m2tp_terminate(void)
#else
__initfunc(void m2tp_terminate(void))
#endif
{
	if (!m2tp_initialized)
		return;
	m2tp_initialized = 0;
#ifndef LIS_REGISTERED
	if (m2tp_minfo.mi_idnum)
		if ((sdt_minfo.mi_idnum = lis_unregister_strmod(&m2tp_info))) {
			cmn_err(CE_WARN, "sdt: couldn't unregister as module!\n");
		}
#endif
};

/*
 *  =========================================================================
 *
 *  LINUX KERNEL MODULE INITIALIZATION
 *
 *  =========================================================================
 */

#ifdef MODULE
int init_module(void)
{
	m2tp_init();
	return (0);
}
void cleanup_module(void)
{
	sctp_terminate();
	return;
}
#endif


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

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

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