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/tcap/tcap_upper.c#ident "@(#) $RCSfile: tcap_upper.c,v $ $Name: $($Revision: 0.8.2.2 $) $Date: 2003/04/03 19:51:40 $" static char const ident[] = "$RCSfile: tcap_upper.c,v $ $Name: $($Revision: 0.8.2.2 $) $Date: 2003/04/03 19:51:40 $"; #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 "../debug.h" #include "../bufq.h" #include "tcap.h" #include "tcap_data.h" #include "tcap_msgs.h" #include "tcap_ctrl.h" #include "tcap_prov.h" #include "../sccpi/sccp_user.h" /* * ========================================================================= * * TCAP-User --> TCAP (Downstream Primitives received from upstream) * * ========================================================================= */ static inline int tc_error_reply(queue_t * q, mblk_t * pdu, int prim, int err) { mblk_t *mp; if ((mp = tc_error_ack(prim, err))) { freemsg(pdu); qreply(q, mp); return (0); } return (-ENOBUFS); } /* * TCAP Options parsing. */ static int parse_options(queue_t * q, mblk_t * pdu, int prim, caddr_t opt_ptr, size_t opt_len) { int err; tcap_t *tcap = (tcap_t *) q->q_ptr; (void) err; (void) tcap; if (opt_len && opt_len < sizeof(uint32_t)) return tc_error_reply(q, pdu, prim, TCBADOPT); /* * Decode the options.... */ return (0); } /* * TR_INFO_REQ 0 - Information request * --------------------------------------------------------------- */ static int tr_info_req(queue_t * q, mblk_t * pdu) { int err; tcap_t *tcap = (tcap_t *) q->q_ptr; TR_info_req_t *p = (TR_info_req_t *) pdu->b_rptr; (void) err; (void) tcap; (void) p; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_reply(q, pdu, TR_INFO_REQ, -EMSGSIZE); /* * TODO: process primitive... */ freemsg(pdu); return (0); } /* * TR_BIND_REQ 1 - Bind to network address * --------------------------------------------------------------- */ static int tr_bind_req(queue_t * q, mblk_t * pdu) { int err; tcap_t *tcap = (tcap_t *) q->q_ptr; TR_bind_req_t *p = (TR_bind_req_t *) pdu->b_rptr; (void) err; (void) tcap; (void) p; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_reply(q, pdu, TR_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tcap->state = NS_IDLE; freemsg(pdu); return (0); } /* * TR_UNBIND_REQ 2 - Unbind from network address * --------------------------------------------------------------- */ static int tr_unbind_req(queue_t * q, mblk_t * pdu) { int err; tcap_t *tcap = (tcap_t *) q->q_ptr; TR_unbind_req_t *p = (TR_unbind_req_t *) pdu->b_rptr; (void) err; (void) tcap; (void) p; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_reply(q, pdu, TR_UNBIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tcap->state = NS_IDLE; freemsg(pdu); return (0); } /* * TR_OPTMGMT_REQ 5 - Options management request * --------------------------------------------------------------- */ static int tr_optmgmt_req(queue_t * q, mblk_t * pdu) { int err; tcap_t *tcap = (tcap_t *) q->q_ptr; TR_optmgmt_req_t *p = (TR_optmgmt_req_t *) pdu->b_rptr; (void) err; (void) tcap; (void) p; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_reply(q, pdu, TR_OPTMGMT_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tcap->state = NS_IDLE; freemsg(pdu); return (0); } /* * TR_UNI_REQ 7 - Unidirectional request * --------------------------------------------------------------- */ static int tr_uni_req(queue_t * q, mblk_t * pdu) { int err; tcap_t *tcap = (tcap_t *) q->q_ptr; TR_uni_req_t *p = (TR_uni_req_t *) pdu->b_rptr; (void) err; (void) tcap; (void) p; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_reply(q, pdu, TR_UNI_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tcap->state = NS_IDLE; freemsg(pdu); return (0); } /* * TR_BEGIN_REQ 7 - Begin transaction request * --------------------------------------------------------------- */ static int tr_begin_req(queue_t * q, mblk_t * pdu) { int err; tcap_t *tcap = (tcap_t *) q->q_ptr; TR_begin_req_t *p = (TR_begin_req_t *) pdu->b_rptr; (void) err; (void) tcap; (void) p; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_reply(q, pdu, TR_BEGIN_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tcap->state = NS_IDLE; freemsg(pdu); return (0); } /* * TR_BEGIN_RES 8 - Begin transaction response * --------------------------------------------------------------- */ static int tr_begin_res(queue_t * q, mblk_t * pdu) { int err; tcap_t *tcap = (tcap_t *) q->q_ptr; TR_begin_res_t *p = (TR_begin_res_t *) pdu->b_rptr; (void) err; (void) tcap; (void) p; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_reply(q, pdu, TR_BEGIN_RES, -EMSGSIZE); /* * TODO: process primitive... */ tcap->state = NS_IDLE; freemsg(pdu); return (0); } /* * TR_CONT_REQ 9 - Continue transaction request * --------------------------------------------------------------- */ static int tr_cont_req(queue_t * q, mblk_t * pdu) { int err; tcap_t *tcap = (tcap_t *) q->q_ptr; TR_cont_req_t *p = (TR_cont_req_t *) pdu->b_rptr; (void) err; (void) tcap; (void) p; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_reply(q, pdu, TR_CONT_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tcap->state = NS_IDLE; freemsg(pdu); return (0); } /* * TR_END_REQ 10 - End transaction request * --------------------------------------------------------------- */ static int tr_end_req(queue_t * q, mblk_t * pdu) { int err; tcap_t *tcap = (tcap_t *) q->q_ptr; TR_end_req_t *p = (TR_end_req_t *) pdu->b_rptr; (void) err; (void) tcap; (void) p; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_reply(q, pdu, TR_END_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tcap->state = NS_IDLE; freemsg(pdu); return (0); } /* * TR_ABORT_REQ 11 - Abort transaction request * --------------------------------------------------------------- */ static int tr_abort_req(queue_t * q, mblk_t * pdu) { int err; tcap_t *tcap = (tcap_t *) q->q_ptr; TR_abort_req_t *p = (TR_abort_req_t *) pdu->b_rptr; (void) err; (void) tcap; (void) p; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_reply(q, pdu, TR_ABORT_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tcap->state = NS_IDLE; freemsg(pdu); return (0); } int (*tr_dprim[]) (queue_t *, mblk_t *) = { #define USER_DSTR_FIRST TR_INFO_REQ tr_info_req, /* TR_INFO_REQ 0 - Information request */ tr_bind_req, /* TR_BIND_REQ 1 - Bind to network address */ tr_unbind_req, /* TR_UNBIND_REQ 2 - Unbind from network address */ tr_optmgmt_req, /* TR_OPTMGMT_REQ 5 - Options management request */ tr_uni_req, /* TR_UNI_REQ 6 - Unidirectional request */ tr_begin_req, /* TR_BEGIN_REQ 7 - Begin transaction request */ tr_begin_res, /* TR_BEGIN_RES 8 - Begin transaction response-Continue request */ tr_cont_req, /* TR_CONT_REQ 9 - Continue transaction request */ tr_end_req, /* TR_END_REQ 10 - End transaction request */ tr_abort_req /* TR_ABORT_REQ 11 - Abort transaction request */ #define USER_DSTR_LAST TR_ABORT_REQ }; static int tcap_w_data(queue_t * q, mblk_t * pdu) { tcap_t *tcap = (tcap_t *) q->q_ptr; if (tcap->state != TRS_DATA_XFER) { /* * If we are not in a connection oriented state, we return an * M_ERROR on all subsequent write operations on the stream * will fail. */ mblk_t *mp; if (!(mp = m_error(0, EPROTO))) return (-ENOBUFS); qreply(q, mp); freemsg(pdu); return (0); } /* * This is an TCAP data message from above. This is only valid in * connection-oriented states. */ /* * TODO: pass data message to SCCP. */ return (0); } static int tcap_w_proto(queue_t * q, mblk_t * mp) { uint32_t prim = *((uint32_t *) mp->b_rptr); if (USER_DSTR_FIRST <= prim && prim <= USER_DSTR_LAST && tr_dprim[prim]) return (*tr_dprim[prim]) (q, mp); /* * FIXME: We should probably send an M_ERROR here. */ return (-EOPNOTSUPP); } /* * ========================================================================= * * M_CTL Processing * * ========================================================================= * We have no defined module to module controls for either TCAP or SCCP. */ static int tcap_w_ctl(queue_t * q, mblk_t * pdu) { /* * FIXME: Remove this TCAP-User from the SCCP-Provider... */ return m_error_reply(q, pdu, EFAULT, EFAULT); } /* * ========================================================================= * * M_IOCTL Processing * * ========================================================================= * * I_LINK, I_PLINK Handling * ------------------------------------------------------------------------- * SCCP Provider streams can be linked under the TCAP multiplexing driver. We * permit normal TCAP streams to perform an I_LINK of SCCP Provider streams * under the SCCP driver, but we only permit the TCAP control stream to * perform an I_PLINK of SCCP Provider streams. This is so that if the * configuration daemon crashes it can come back an the configuration has not * been destroyed. * * When linked, we generate an N_INFO_REQ downstream to the newly linked SCCP * Provider. The purpose of this is to discover the response from the * provider as to which state the provider is currently in and which * addresses the SCCP Provider might be currently bound to. This permits the * caller to bind the SCCP Provider. */ static inline int tcap_i_link(queue_t * q, mblk_t * pdu, struct linkblk *lp) { mblk_t *mp; sccp_t *sccp; queue_t *lq; // ensure( lp, return(-EFAULT) ); lq = RD(lp->l_qbot); if (!(mp = sccp_info_req())) return (-ENOBUFS); if (!(sccp = kmalloc(sizeof(*sccp), GFP_KERNEL))) { freemsg(mp); return (-ENOMEM); } bzero(sccp, sizeof(*sccp)); sccp->q = RD(lq); sccp->lmq = RD(q); sccp->muxid = lp->l_index; sccp->state = NS_UNBND; RD(lq)->q_ptr = WR(lq)->q_ptr = sccp; putnext(WR(lq), mp); /* get info on SCCP provider */ if (sccp->state != NS_IDLE) { /* sccp providers must be prebound */ RD(lq)->q_ptr = WR(lq)->q_ptr = NULL; kfree(sccp); return (-EINVAL); } if ((sccp->next = tcap_links)) sccp->next->prev = &sccp->next; sccp->prev = &tcap_links; return (lp->l_index); } /* * I_UNLINK, I_PUNLINK Handling * ------------------------------------------------------------------------- * When we unlink, if we still have referencing upper TCAP-User streams, we * send each of the a M_HANGUP message indicating the loss of the connection. * This might result in a SIG_PIPE signal being sent to the process if the * TCAP-User is a stream head. */ static inline int tcap_i_unlink(queue_t * q, struct linkblk *lp) { int err; sccp_t *sccp; uint muxid; // ensure( lp, return(-EFAULT) ); muxid = lp->l_index; for (sccp = tcap_links; sccp && sccp->muxid != muxid; sccp = sccp->next); if (!sccp) return (-EINVAL); if ((err = m_flush_all(q, NULL, FLUSHW, 0))) return (err); if ((err = m_hangup_all(q, NULL))) return (err); if (sccp->state == NS_IDLE) { mblk_t *mp; if (!(mp = sccp_unbind_req())) return (-ENOBUFS); sccp->state = NS_WACK_UREQ; putnext(WR(sccp->q), mp); } if ((*(sccp->prev) = sccp->next)) sccp->next->prev = sccp->prev; sccp->q->q_ptr = WR(sccp->q)->q_ptr = NULL; kfree(sccp); return (0); } static int tcap_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 = (struct iocblk *) mp->b_wptr; int cmd = iocp->ioc_cmd; switch (_IOC_TYPE(cmd)) { case __SID: switch (cmd) { case I_PLINK: if (RD(q) != tcap_lmq) { ret = -EPERM; break; } case I_LINK: ret = tcap_i_link(q, mp, arg); break; case I_PUNLINK: if (RD(q) != tcap_lmq) { ret = -EPERM; break; } case I_UNLINK: ret = tcap_i_unlink(q, arg); break; } break; case TCAP_IOC_MAGIC: #if 0 if (iocp->ioc_count >= _IOC_SIZE(cmd)) { int nr = _IOC_NR(cmd); ret = -EOPNOTSUPP; if (0 <= nr && nr < sizeof(tcap_ioctl) / sizeof(int (*)(void)) && tcap_ioctl[nr]) ret = (*tcap_ioctl[nr]) (q, cmd, arg); } #endif 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); } /* * ========================================================================= * * QUEUE PUT and SERVICE routines * * ========================================================================= */ /* * WRITE PUT and SERVICE (Message from above TCAP-User --> TCAP) * ------------------------------------------------------------------------- */ int tcap_u_w_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: return tcap_w_data(q, mp); case M_PROTO: case M_PCPROTO: return tcap_w_proto(q, mp); case M_CTL: return tcap_w_ctl(q, mp); case M_FLUSH: return tcap_w_flush(q, mp); case M_IOCTL: return tcap_w_ioctl(q, mp); } return (-EOPNOTSUPP); }
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |