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/m3ua/m3ua_mtpu.c#ident "@(#) $RCSfile: m3ua_mtpu.c,v $ $Name: $($Revision: 0.8.2.2 $) $Date: 2003/04/03 19:50:31 $" static char const ident[] = "$RCSfile: m3ua_mtpu.c,v $ $Name: $($Revision: 0.8.2.2 $) $Date: 2003/04/03 19:50:31 $"; #define __NO_VERSION__ #include <linux/config.h> #include <linux/version.h> #ifdef MODVERSIONS #include <linux/modversions.h> #endif #include <sys/stream.h> #include <sys/stropts.h> #include <sys/cmn_err.h> #include "m3ua.h" #include "m3ua_data.h" #include "m3ua_ctrl.h" #include "m3ua_msg.h" /* * ========================================================================= * * MTP-User --> M3UA (ASP) Downstream Primitives * * ========================================================================= */ static inline int mtp_error_reply(queue_t * q, mblk_t * pdu, int prim, int err) { mblk_t *mp; if ((mp = mtp_error_ack(prim, err))) { freemsg(pdu); qreply(q, mp); return (0); } return (-ENOBUFS); } static inline int mtp_uderror_reply(queue_t * q, mblk_t * pdu, int err) { mblk_t *mp; N_unitdata_req_t *p = (N_unitdata_req_t *) pdu->b_rptr; if ((mp = mtp_uderror_ind(err, p->DEST_length ? (mtp_addr_t *) (pdu->b_rptr + p->DEST_offset) : 0, p->SRC_length ? (mtp_addr_t *) (pdu->b_rptr + p->SRC_offset) : 0, pdu->b_cont))) { freeb(pdu); qreply(q, mp); return (0); } return (-ENOBUFS); } static int parse_options(queue_t * q, mblk_t * pdu, int prim, caddr_t opt_ptr, size_t opt_len) { mtp_t *mtp = (mtp_t *) q->q_ptr; if (opt_len < sizeof(uint32_t)) return mtp_error_reply(q, pdu, prim, NBADQOSTYPE); switch (*((ulong *) opt_ptr)) { case N_QOS_SEL_MTP: { N_qos_sel_mtp_t *p = (N_qos_sel_mtp_t *) opt_ptr; if (prim == N_OPTMGMT_REQ) goto parse_options_badqostype; if (opt_len < sizeof(*p)) goto parse_options_badqostype; if (prim != N_CONN_REQ && prim != N_CONN_RES && prim != N_UNITDATA_REQ) goto parse_options_badqostype; if (p->sls == QOS_UNKNOWN) mtp->sls = mtp_next_sls_value++; else mtp->sls = p->sls; if (p->mp == QOS_UNKNOWN) mtp->mp = 0; else mtp->mp = p->mp; break; } case N_QOS_OPT_SEL_MTP: { N_qos_opt_sel_mtp_t *p = (N_qos_sel_mtp_t *) opt_ptr; if (prim != N_OPTMGMT_REQ) goto parse_options_badqostype; if (opt_len < sizeof(*p)) goto parse_options_badqostype; break; } default: goto parse_options_badqostype; } return (0); parse_options_badqostype:return mtp_error_reply(q, pdu, prim, NBADQOSTYPE); } /* * N_CONN_REQ 0 - NC request * ------------------------------------------------------------------------- * This request is only valid for ISUP/TUP MTP-Users. When we receive a * connect request, we either have an existing AS which has the indicated * Routing Key or we request that the LM queue create and return an AS. If * the AS exists and is statically provisioned, then we can perform ASPAC * procedures on any associated SGP. If no SGP are associated, we can refuse * the connection request. If SGP are associated we send an ASPAC and wait * for the result before responding with an N_CONN_CON. */ static int mtpu_conn_req(queue_t * q, mblk_t * pdu) { mtp_addr_t *a; mtp_t *mtp = (mtp_t *) q->q_ptr; N_conn_req_t *p = (N_conn_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) goto mtpu_conn_req_emsgsize; if (pdu->b_rptr + p->DEST_offset > pdu->b_wptr - p->DEST_length) goto mtpu_conn_req_badaddr; if (pdu->b_rptr + p->QOS_offset > pdu->b_wptr - p->QOS_length) goto mtpu_conn_req_badopt; if (p->DEST_length == 0) goto mtpu_conn_req_noaddr; if (p->DEST_length < sizeof(*a)) goto mtpu_conn_req_badaddr; if (mtp->state != NS_IDLE) goto mtpu_conn_req_outstate; if ((err = parse_options(q, pdu, N_CONN_REQ, pdu->b_rptr + p->QOS_offset, p->QOS_length))) return (err); a = (mtp_addr_t *) (pdu->b_rptr + p->DEST_offset); bcopy(a, &mtp->dst, sizeof(*a)); /* * FIXME: There is a couple of things we could do here: * * 1) We could formulate a Routing Key and then check the list of * configured Routing Keys to see if one is associated with a * current AS. * * 2) If we find an RK match but it is not associated with an AS, * yet it is associated with SGPs for dynamic registration, we * can create an AS and attempt to dynamically register the RK * with a REG REQ for those SGP supporting it. * * 3) If we cannot find an RK match, we can ask the LM queue to map * the RK for us. * * 4) If we cannot find an RK match, we can formulate a REG REQ and * send it to all available SGPs. * */ mtp->state = NS_WCON_CREQ; freemsg(pdu); return (0); mtpu_conn_req_emsgsize:return mtp_error_reply(q, pdu, N_CONN_REQ, -EMSGSIZE); mtpu_conn_req_badaddr:return mtp_error_reply(q, pdu, N_CONN_REQ, NBADADDR); mtpu_conn_req_badopt:return mtp_error_reply(q, pdu, N_CONN_REQ, NBADOPT); mtpu_conn_req_noaddr:return mtp_error_reply(q, pdu, N_CONN_REQ, NNOADDR); mtpu_conn_req_outstate:return mtp_error_reply(q, pdu, N_CONN_REQ, NOUTSTATE); } /* * N_DISCON_REQ 2 - NC disconnection request * ------------------------------------------------------------------------- * This request is only valid for ISUP/TUP MTP-Users. When we receive a * disconnect request from the user, we should detach the SS7 stream from the * AS and change the AS state if necessary. If the AS state changes to * INACTIVE from ACTIVE we should perorm ASPIA procedures with each * associated SGP (IPC or UAP) and ASPIA Ack procedures with each associated * ASP (IPC). Reasons are not given. */ static int mtpu_discon_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; size_t dlen = msgdsize(pdu); size_t mlen = pdu->b_wptr - pdu->b_rptr; mtp_t *mpt = (mtp_t *) q->q_ptr; N_discon_req_t *p = (N_discon_req_t *) pdu->b_rptr; if (mlen < sizeof(*p)) goto mtpu_discon_req_emsgsize; if (dlen > 0) goto mtpu_discon_req_baddata; switch (mtp->state) { case NS_WCON_CREQ: /* * FIXME: we are trying to connect, we need to abort * the connection request. */ break; case NS_DATA_XFER: case NS_WRES_RIND: /* * FIXME: initiate disconnect. This consists of * deactivating the SS7 stream and seeing if the AS * is now deactivated. If the AS is inactive as a * result, send ASPIA for the AS. */ default: goto mtp_discon_req_outstate; } freemsg(pdu); return (0); mtpu_discon_req_outstate:return mtp_error_reply(q, pdu, N_DISCON_REQ, NOUTSTATE); mtpu_discon_req_baddata:return mtp_error_reply(q, pdu, N_DISCON_REQ, NBADDATA); mtpu_discon_req_emsgsize:return mtp_error_reply(q, pdu, N_DISCON_REQ, -EMSGSIZE); } /* * N_DATA_REQ 3 - Connection-Mode data transfer request * ------------------------------------------------------------------------- * This request is only valid for ISUP/TUP MTP-Users. If the interface is in * the NS_DATA_XFER state, we select an SGP and send DATA messages, otherwise * we return an error or N_DISCON_IND. */ static int mtpu_data_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; uint more; size_t dlen = msgdsize(pdu); size_t mlen = pdu->b_wptr - pdu->b_rptr; mtp_t *mtp = (mtp_t *) q->q_ptr; N_data_req_t *p = (N_data_req_t *) pdu->b_rptr; if (mlen < sizeof(*p)) goto mtpu_data_req_eproto; if (!dlen || dlen > mtp->nidu) goto mtpu_data_req_eproto; if (mtp->state == NS_IDLE || sccp->state == NS_WRES_RIND) goto mtpu_data_req_ignore; if (mtp->state != NS_DATA_XFER) goto mtpu_data_req_eproto; if (!pdu->b_cont) goto mtpu_data_req_eproto; if (p->DATA_xfer_flags & N_RC_FLAG) goto mtpu_data_req_eproto; if (p->DATA_xfer_flags & N_MORE_DATA_FLAG) { if (!mtp->assemble) mtp->assemble = pdu->b_cont; else linkb(mtp->assemble, pdu->b_cont); freeb(pdu); return (0); } if (mtp->assemble) { linkb(mtp->assemble, pdu->b_cont); pdu->b_cont = mtp->assemble; mtp->assemble = NULL; if (msgdsize(pdu) > mtp->nidu) goto mtpu_data_req_eproto; } /* * FIXME: select an SGP and formulate a DATA message and send * it to the SGP on a selected stream. */ putq(q, mp); freeb(pdu); return (0); mtpu_data_req_ignore:freemsg(pdu); return (0); mtpu_data_req_eproto:return m_error_reply(q, pdu, EPROTO, EPROTO); } /* * N_INFO_REQ 5 - Information Request * ------------------------------------------------------------------------- * Valid for both CNLS and CONS. We always return current information * associated with the interface in an N_INFO_ACK. */ static int mtpu_info_req(queue_t * q, mblk_t * mp) { } /* * N_BIND_REQ * ------------------------------------------------------------------------- * Depending on CNLS or CONS. CNLS we check for Routing Keys associated with * ASPs which match the requested bind. If no AS is found with the requested * bind, we RK or perform a REG REQ to associated SGP. If an AS is found, we * or REG REQ is successful, we perform ASPAC procedures to associated SGP. * For CONS, we bind the local address and return N_BIND_ACK. */ static int mtpu_bind_req(queue_t * q, mblk_t * mp) { mblk_t *rp; mtp_addr_t *a; mtp_t *mtp = (mtp_t *) q->q_ptr; N_bind_req_t *p = (N_bind_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) goto mtpu_bind_req_emsgsize; if (pdu->b_rptr + p->ADDR_offset > pdu->b_wptr - p->ADDR_length) goto mtpu_bind_req_badaddr; if (pdu->b_rptr + p->PROTOID_offset > pdu->b_wptr - p->PROTOID_length) goto mtpu_bind_req_badprot; if (p->ADDR_length == 0) goto mtpu_bind_req_noaddr; if (p->ADDR_length < sizeof(*a)) goto mtpu_bind_req_badaddr; if (mtp->state != NS_UNBND) goto mtpu_conn_req_outstate; a = (mtp_addr_t *) (pdu->b_rptr + p->ADDR_offset); bcopy(a, &mtp->src, sizeof(*a)); /* * FIXME: There is a couple of things we could do here: * * 1) We could formulate a RK and then check the list of configured * RKs to see if one is associated with a current AS. * * 2) If we find an RK match but it is not associated with an AS, * yet it is associated with SGPs for dynamic registration, we * can create an AS and attempt to dynamically register the RK * with a REG REQ for those SGP supporting it. * * 3) If we cannot find an RK match, we can ask the LM queue to map * the RK for us. * * 4) If we cannot find an RK match, we can formulate a REG REQ and * send it to all available SGPs. */ mtp->state = NS_IDLE; freemsg(pdu); return (0); mtpu_bind_req_emsgsize:return mtp_error_reply(q, pdu, N_BIND_REQ, -EMSGSIZE); mtpu_bind_req_badaddr:return mtp_error_reply(q, pdu, N_BIND_REQ, NBADADDR); mtpu_bind_req_badprot:return mtp_error_reply(q, pdu, N_BIND_REQ, NBADPROTO); mtpu_bind_req_noaddr:return mtp_error_reply(q, pdu, N_BIND_REQ, NNOADDR); mtpu_conn_req_outstate:return mtp_error_reply(q, pdu, N_BIND_REQ, NOUTSTATE); } /* * N_UNBIND_REQ * ------------------------------------------------------------------------- * We always just change state and return a N_OK_ACK. For CNLS, unbinding * the stream also needs to deactivate it. If the associated AS becomes * inactive due to this, the ASPIA procedures may need to be performed. */ static int mtpu_unbind_req(queue_t * q, mblk_t * pdu) { mblk_t *rp; mtp_t *mtp = (mtp_t *) q->q_ptr; N_unbind_req_t *p = (N_unbind_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) goto mtpu_unbind_req_emsgsize; if (mtp->state != NS_IDLE) goto mtpu_unbind_req_outstate; if (!(rp = mtp_ok_ack(N_UNBIND_REQ))) return (-ENOBUFS); mtp->state = NS_WACK_UREQ; qreply(q, mp); mtp->state = NS_UNBND; freemsg(pdu); return (0); mtpu_unbind_req_emsgsize:return mtp_error_reply(q, pdu, N_UNBIND_REQ, -EMSGSIZE); mtpu_unbind_req_outstate:return mtp_error_reply(q, pdu, N_UNBIND_REQ, NOUTSTATE); } /* * N_UNITDATA_REQ * ------------------------------------------------------------------------- * This request is only valid for non-ISUP/TUP MTP-Users. If we are in * NS_IDLE state we formulate a DATA message and send it on to a selected * SGP (or return error). */ static int mtpu_unitdata_req(queue_t * q, mblk_t * mp) { mtp_addr_t *src, *dst; N_qos_sel_mtp_t *qos; mtp_t *mtp = (mtp_t *) q->q_ptr; size_t dlen = msgdsize(pdu); size_t mlen = pdu->b_wptr - pdu->b_rptr; N_unitdata_req_t *p = (N_unitdata_req_t *) pdu->b_rptr; if (mlen < sizeof(*p) || mlen < p->DEST_offset + p->DEST_length || mlen < p->SRC_offset + p->SRC_length || mlen < p->QOS_offset + p->QOS_length) goto mtpu_unitdata_req_emsgsize; if (mtp->state != NS_IDLE || dlen > mtp->nidu) goto mtpu_unitdata_req_eproto; if (!dlen) goto mtpu_unitdata_req_baddata; if (!p->QOS_length || p->QOS_length < sizeof(*qos)) goto mtpu_unitdata_req_badqos; else { qos = (N_qos_sel_mtp_t *) (pdu->b_rptr + p->QOS_offset); if (qos->n_qos_type != N_QOS_SEL_MTP) goto mtpu_unitdata_req_badqos; /* fill in missing QOS parameters for user */ if (qos->sls == -1) mtp->sls = mtp_next_sls_value++; if (qos->mp == -1) mtp->mp = 0; } if (!p->SRC_length) { /* implicit source address */ src = &mtp->src; } else { /* check provided source address */ src = (mtp_addr_t *) (pdu->b_rptr + p->SRC_offset); if (p->SRC_length < sizeof(*src)) goto mtpu_unitdata_req_badaddr; /* fill in PC for user */ if (!src->ni || src->ni == -1) src->ni = mtp->src.ni; if (!src->pc || src->pc == -1) src->pc = mtp->src.pc; if (!src->si || src->si == -1) src->si = mtp->src.si; } if (!p->DEST_length) { /* destination address is mandatory */ goto mtp_unitdata_req_badaddr; } else { /* check provided destination address */ dst = (mtp_addr_t *) (pdu->b_rptr + p->DEST_offset); if (p->DEST_length < sizeof(*dst)) goto mtpu_unitdata_req_badaddr; if (!dst->ni || dst->ni == -1) dst->ni = mtp->src.ni; if (!dst->si || dst->si == -1) dst->si = mtp->src.si; if (!dst->ni || dst->ni == -1) goto mtpu_unitdata_req_badaddr; } /* * FIXME: Package up the data, select an SGP, stream and transmit * message. */ freeb(pdu); return (0); mtp_unitdata_req_badaddr:return mtp_uderror_reply(q, pdu, NBADADDR); mtp_unitdata_req_badqos:return mtp_uderror_reply(q, pdu, NBADOPT); mtp_unitdata_req_baddata:return mtp_uderror_reply(q, pdu, NBADDATA); mtp_unitdata_req_emsgsize:return mtp_error_reply(q, pdu, N_UNITDATA_REQ, -EMSGSIZE); mtp_unitdata_req_eproto:return m_error_reply(q, pdu, EPROTO, EPROTO); } /* * N_OPTMGMT_REQ * ------------------------------------------------------------------------- * This depends on a number of factors. Some requests are local, some * require interaction with the SGP. */ static int mtpu_optmgmt_req(queue_t * q, mblk_t * mp) { } /* * N_RESET_RES * ------------------------------------------------------------------------- * This request is only valid for ISUP/TUP MTP-Users. This indicates that * the User has accepted a reset indication. We change state back to * NS_DATA_XFER. */ static int mtpu_reset_res(queue_t * q, mblk_t * mp) { int err; mblk_t *rp; size_t mlen = pdu->b_wptr - pdu->b_rptr; mtp_t *mtp = (mtp_t *) q->q_ptr; N_reset_res_t *p = (N_reset_res_t *) pdu->b_rptr; if (mlen < sizeof(*p)) goto mtpu_reset_res_emsgsize; if (mtp->state == NS_IDLE) goto mtpu_reset_req_ignore; if (mtp->state != NS_WRES_RIND) goto mtpu_reset_req_outstate; if (!(rp = mtp_ok_ack(N_RESET_RES))) return (-ENOBUFS); /* * TODO: more... */ mtp->state = NS_WACK_RRES; qreply(q, rp); mtp->state = NS_DATA_XFER; /* on success */ mtpu_reset_res_ignore: freemsg(pdu); return (0); mtpu_reset_res_emsgsize:return mtp_reply_error(q, pdu, N_RESET_RES, -EMSGSIZE); mtpu_reset_res_outstate:return mtp_reply_error(q, pdu, N_RESET_RES, NOUTSTATE); } static int (*mtpu_dstr_prim[]) (queue_t *, mblk_t *) = { #define MTP_DSTR_FIRST N_CONN_REQ mtpu_conn_req, /* N_CONN_REQ 0 */ NULL, /* N_CONN_RES 1 */ mtpu_discon_req, /* N_DISCON_REQ 2 */ mtpu_data_req, /* N_DATA_REQ 3 */ NULL, /* N_EXDATA_REQ 4 */ mtpu_info_req, /* N_INFO_REQ 5 */ mtpu_bind_req, /* N_BIND_REQ 6 */ mtpu_unbind_req, /* N_UNBIND_REQ 7 */ mtpu_unitdata_req, /* N_UNITDATA_REQ 8 */ mtpu_optmgmt_req, /* N_OPTMGMT_REQ 9 */ NULL, /* 10 */ NULL, /* N_CONN_IND 11 */ NULL, /* N_CONN_CON 12 */ NULL, /* N_DISCON_IND 13 */ NULL, /* N_DATA_IND 14 */ NULL, /* N_EXDATA_IND 15 */ NULL, /* N_INFO_ACK 16 */ NULL, /* N_BIND_ACK 17 */ NULL, /* N_ERROR_ACK 18 */ NULL, /* N_OK_ACK 19 */ NULL, /* N_UNITDATA_IND 20 */ NULL, /* N_UDERROR_IND 21 */ NULL, /* 22 */ NULL, /* N_DATACK_REQ 23 */ NULL, /* N_DATACK_IND 24 */ NULL, /* N_RESET_REQ 25 */ NULL, /* N_RESET_IND 26 */ mtpu_reset_res, /* N_RESET_RES 27 */ #define MTP_DSTR_LAST N_RESET_RES NULL, /* N_RESET_CON 28 */ }; /* * M_DATA Processing * ------------------------------------------------------------------------- */ static int mtpu_w_data(queue_t * q, mblk_t * mp) { int err; mblk_t *np; if (!(np = mtpu_data(q, mp))) return (-ENOBUFS); // if ( (err = m3ua_as_write(q, np)) ) // return(err); return (0); } /* * M_PROTO, M_PCPROTO Processing * ------------------------------------------------------------------------- */ static int mtpu_w_proto(queue_t * q, mblk_t * mp) { int prim = *((long *) mp->b_wptr); if (MTP_DSTR_FIRST <= prim && prim <= MTP_DSTR_LAST && mtpu_dstr_prim[prim]) return ((*mtpu_dstr_prim[prim]) (q, mp)); return (-EOPNOTSUPP); } /* * M_ERROR, M_HANGUP Processing * ------------------------------------------------------------------------- */ /* * M_FLUSH Processing * ------------------------------------------------------------------------- */ static int (*mtpu_w_ops[]) (queue_t *, mblk_t *) = { mtpu_w_data, /* M_DATA */ mtpu_w_proto, /* M_PROTO */ NULL, /* M_BREAK */ NULL, /* M_CTL */ NULL, /* M_DELAY */ NULL, /* M_IOCTL */ NULL, /* M_PASSFP */ NULL, /* M_RSE */ NULL, /* M_SETOPTS */ NULL, /* M_SIG */ NULL, /* M_COPYIN */ NULL, /* M_COPYOUT */ NULL, /* M_ERROR */ mtpu_w_flush, /* M_FLUSH */ NULL, /* M_HANGUP */ NULL, /* M_IOCACK */ NULL, /* M_IOCNAK */ NULL, /* M_IOCDATA */ mtpu_w_proto, /* M_PCPROTO */ NULL, /* M_PCRSE */ NULL, /* M_PCSIG */ NULL, /* M_READ */ NULL, /* M_STOP */ NULL, /* M_START */ NULL, /* M_STARTI */ NULL /* M_STOPI */ }; struct ops mtpu_ops = { NULL, /* read operations */ &mtpu_w_ops /* write operations */ };
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |