OpenSS7 SS7 for the Common Man |
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |
||||||||||||||||||||||||||
Home | Overview | Status | News | Documentation | Resources | About | |||||||||||||||||||||
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); }
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |