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/m3ua.c#ident "@(#) $RCSfile: m3ua.c,v $ $Name: $($Revision: 0.8.2.3 $) $Date: 2003/04/03 19:51:06 $" static char const ident[] = "$RCSfile: m3ua.c,v $ $Name: $($Revision: 0.8.2.3 $) $Date: 2003/04/03 19:51:06 $"; #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/npi.h> #include <sys/npi_sctp.h> #include <sys/npi_mtp.h> #include "../debug.h" #include "../bufq.h" #include "ua_data.h" #include "ua.h" #include "ua_msgs.h" #include "m3ua_msgs.h" #ifndef M3UA_CMAJOR #define M3UA_CMAJOR 111 #endif #ifndef M3UA_NMAJOR #define M3UA_NMAJOR 1 #endif #ifndef M3UA_NMINOR #define M3UA_NMINOR 256 #endif #ifndef INT #define INT void #endif #ifndef M3UA_PPI #define M3UA_PPI 3 #endif struct drv m3ua_driver; /* * ========================================================================= * * STREAMS Message Handling * * ========================================================================= */ /* * ------------------------------------------------------------------------- * * MTP User (MTPU) Stream Message Handling * * ------------------------------------------------------------------------- */ /* * ------------------------------------------------------------------------- * * NPI Provider (M3UA) --> NPI User (MTP-User) Primitives * * ------------------------------------------------------------------------- */ /* * N_CONN_IND 11 - Incoming connection indication * ------------------------------------------------------------- */ STATIC INLINE int mtpu_conn_ind(queue_t * q, mblk_t * cp) { (void) q; (void) cp; ptrace(("SWERR: invalid connection indication\n")); return (-EFAULT); } /* * N_CONN_CON 12 - Connection established * --------------------------------------------------------- */ STATIC INLINE int mtpu_conn_con(queue_t * q, uint pcls, uint flags, m3ua_addr_t * res, mblk_t * dp) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_conn_con_t *p; N_qos_sel_conn_mtp_t *qos; size_t res_len = res ? sizeof(*res) + res->alen : 0; size_t qos_len = sizeof(*qos); size_t msg_len = sizeof(*p) + PAD4(res_len) + qos_len; if ((sp->i_state != NS_WCON_CREQ)) goto efault; if (!canputnext(sp->rq)) 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 = N_CONN_CON; p->RES_length = res_len; p->RES_offset = res_len ? sizeof(*p) : 0; p->CONN_flags = flags; p->QOS_length = qos_len; p->QOS_offset = qos_len ? sizeof(*p) + PAD4(res_len) : 0; if (res_len) { bcopy(res, mp->b_wptr, res_len); mp->b_wptr += PAD4(res_len); } if (qos_len) { qos = ((typeof(qos)) mp->b_wptr)++; qos->n_qos_type = N_QOS_SEL_CONN_MTP; } mp->b_cont = dp; sp->i_state = NS_DATA_XFER; putnext(sp->rq, 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: confirmation in wrong state\n")); goto error; error: return (err); } /* * N_DISCON_IND 13 - NC disconnected * --------------------------------------------------------- * For M3UA the responding address in a NC connection refusal is always the * destination address (after GTT) to which the NC connection was sent. */ STATIC INLINE int mtpu_discon_ind(queue_t * q, ulong orig, long reason, m3ua_addr_t * res, mblk_t * seq, mblk_t * dp) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_discon_ind_t *p; size_t res_len = res ? sizeof(*res) + res->alen : 0; size_t msg_len = sizeof(*p) + PAD4(res_len); if ((1 << sp->i_state) & ~(NSF_WCON_CREQ | NSF_WRES_CIND | NSF_DATA_XFER | NSF_WCON_RREQ | NSF_WRES_RIND)) goto efault; if (!canputnext(sp->rq)) 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 = N_DISCON_IND; p->DISCON_orig = orig; p->DISCON_reason = reason; p->RES_length = res_len; p->RES_offset = res_len ? sizeof(*p) : 0; p->SEQ_number = (ulong) seq; if (res_len) { bcopy(res, mp->b_wptr, res_len); mp->b_wptr += PAD4(res_len); } if (seq) { bufq_unlink(&sp->conq, seq); freemsg(seq); } sp->i_state = NS_IDLE; mp->b_cont = dp; putnext(sp->rq, 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); } /* * N_DATA_IND 14 - Incoming connection-mode data indication * ----------------------------------------------------------------- */ STATIC INLINE int mtpu_data_ind(queue_t * q, uint more, mblk_t * dp) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_data_ind_t *p; N_qos_sel_data_mtp_t *qos; const size_t qos_len = sizeof(*qos); const size_t msg_len = sizeof(*p) + qos_len; if ((1 << sp->i_state) & ~(NSF_DATA_XFER)) goto efault; if (!canputnext(sp->rq)) 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 = N_DATA_IND; p->DATA_xfer_flags = (more ? N_MORE_DATA_FLAG : 0); qos = ((typeof(qos)) mp->b_wptr)++; qos->n_qos_type = N_QOS_SEL_DATA_MTP; qos->sls = sp->sls; /* FIXME */ qos->mp = sp->mp; /* FIXME */ fixme(("Get the SLS and MP from the arguments to this function\n" "Actually, it may be worthwhile not delivering QOS\n")); mp->b_cont = dp; putnext(sp->rq, 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); } /* * N_EXDATA_IND 15 - Incoming expedited data indication * ----------------------------------------------------------- * The lack of a more flag presents a challenge. Perhaps we shouldbe * reassembling expedited data. */ STATIC INLINE int mtpu_exdata_ind(queue_t * q, uint more, mblk_t * dp) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_exdata_ind_t *p; N_qos_sel_data_mtp_t *qos; const size_t qos_len = sizeof(*qos); const size_t msg_len = sizeof(*p) + qos_len; if ((1 < sp->i_state) & ~(NSF_DATA_XFER)) goto efault; if (!canputnext(sp->rq)) 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 = N_EXDATA_IND; qos = ((typeof(qos)) mp->b_wptr)++; qos->n_qos_type = N_QOS_SEL_DATA_MTP; qos->sls = sp->sls; /* FIXME */ qos->mp = sp->mp; /* FIXME */ fixme(("Get the SLS and MP from the arguments to this function\n" "Actually, it may be worthwhile not delivering QOS\n")); mp->b_cont = dp; mp->b_cont = dp; putnext(sp->rq, 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); } /* * N_INFO_ACK 16 - Information Acknowledgement * --------------------------------------------------------- */ STATIC INLINE int mtpu_info_ack(queue_t * q) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_info_ack_t *p; N_qos_sel_info_mtp_t *qos; N_qos_range_info_mtp_t *qor; m3ua_addr_t *add, *ss = &sp->src; uint32_t *pro; size_t add_len = ss ? sizeof(*ss) + ss->alen : 0; size_t qos_len = sizeof(*qos); size_t qor_len = sizeof(*qor); size_t pro_len = sizeof(uint32_t); size_t msg_len = sizeof(*p) + add_len + qos_len + qor_len + pro_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 = N_INFO_ACK; p->NSDU_size = -1; /* no limit on NSDU size */ p->ENSDU_size = -1; /* no limit on ENSDU size */ p->CDATA_size = -1; /* no limit on CDATA size */ p->DDATA_size = -1; /* no limit on DDATA size */ p->ADDR_size = sizeof(m3ua_addr_t) + M3UA_MAX_ADDR_LENGTH; p->ADDR_length = add_len; p->ADDR_offset = add_len ? sizeof(*p) : 0; p->QOS_length = qos_len; p->QOS_offset = qos_len ? sizeof(*p) + add_len : 0; p->QOS_range_length = qor_len; p->QOS_range_offset = qor_len ? sizeof(*p) + add_len + qos_len : 0; p->OPTIONS_flags = REC_CONF_OPT | EX_DATA_OPT | ((sp->flags & M3UA_FLAG_DEFAULT_RC_SEL) ? DEFAULT_RC_SEL : 0); p->NIDU_size = -1; p->SERV_type = N_CONS | N_CLNS; p->CURRENT_state = sp->i_state; p->PROVIDER_type = N_SUBNET; p->NODU_size = 254; p->PROTOID_length = pro_len; p->PROTOID_offset = pro_len ? sizeof(*p) + add_len + qos_len + qor_len : 0; p->NPI_version = N_VERSION_2; if (add_len) { add = (typeof(add)) mp->b_wptr; bcopy(ss, add, sizeof(*ss) + ss->alen); mp->b_wptr += add_len; } if (qos_len) { qos = ((typeof(qos)) mp->b_wptr)++; qos->n_qos_type = N_QOS_SEL_INFO_MTP; qos->pvar = sp->pvar; qos->popt = sp->popt; } if (qor_len) { qor = ((typeof(qor)) mp->b_wptr)++; qor->n_qos_type = N_QOS_RANGE_INFO_M3UA; qor->sls_range = 0xff; /* FIXME: depends on pvar */ qor->mp_range = 0x03; /* FIXME: depends on pvar and popt */ } if (pro_len) { pro = ((typeof(pro)) mp->b_wptr)++; *pro = 3; } putnext(sp->rq, mp); return (0); enobufs: err = -ENOBUFS; ptrace(("ERROR: No buffers\n")); goto error; error: return (err); } /* * N_INFO_ACK 16 - Information Acknowledgement * ------------------------------------------------------------------------- * This returns various protocol parameter information related to the OpenSS7 * MTP interface. * * TODO: There should be two ways of obtaining the necessar information for * the info ack: 1) the information is statically provisioned and associated * with the SP when the SP is created. 2) the information is obtained by * querying the SS7-P or SGP when the interface is activated. To query the * SS7-P we simply issue an N_INFO_REQ to the SS7-P and collect the results * that we return here. If there is a local provider, we should do this * automagically. To query the SGP, we need to return defaults when the SP * is created, but can obtain informatoin as part of the ASPAC ACK when * draft-bidulock-sigtran-sginfo-00.txt is implemented. */ static int mtpu_info_ack(queue_t * q) { dp_t *dp = (dp_t *) q->q_ptr; as_t *as = dp->as; mblk_t *mp; N_info_ack_t *p; N_qos_sel_info_mtp_t *qos; N_qos_range_mtp_t *qor; uint32_t *pro; mtp_addr_t *rk = as ? as->rk : NULL; size_t alen = (as && rk) ? sizeof(struct mtp_addr) : 0; size_t qlen = sizeof(*qos); size_t rlen = sizeof(*qor); size_t plen = alen ? sizeof(uint32_t) : 0; mtp_addr_t *add = NULL; size_t mlen = sizeof(*p) + alen + qlen + rlen + plen; if (!(mp = ua_allocb(q, mlen, BPRI_MED))) goto enobufs; mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_INFO_ACK; p->NSDU_size = 272; p->ENSDU_size = 0; p->CDATA_size = -2UL; p->DDATA_size = -2UL; p->ADDR_size = sizeof(*add); p->ADDR_length = alen; p->ADDR_offset = alen ? sizeof(*p) : 0; p->QOS_length = qlen; p->QOS_offset = qlen ? sizeof(*p) + alen : 0; p->QOS_range_length = rlen; p->QOS_range_offset = rlen ? sizeof(*p) + alen + qlen : 0; p->OPTIONS_flags = 0; p->NIDU_size = -1UL; p->SERV_type = N_CLNS; p->CURRENT_state = dp->state; p->PROVIDER_type = N_SUBNET; p->NODU_size = 100; p->PROTOID_length = plen; p->PROTOID_offset = plen ? sizeof(*p) + alen + qlen + rlen : 0; p->NPI_version = N_VERSION_2; if (alen) { add = ((typeof(add)) mp->b_wptr)++; add->ni = rk->ni; add->si = rk->si; add->pc = rk->pc; } /* TODO: This field normally contains the Protocol Variant and Protocol Options for MTP. The Protocol variant and options could possibly be implied by the Network Appearance which we will fill into the NI field. This requires some more thought. */ if (qlen) { fixme(("Need to handle qos\n")); qos = ((typeof(qos)) mp->b_wptr)++; qos->pvar = 0; qos->popt = 0; } /* TODO: this field normall contains the SLS range and the MP range which are permissible for this MTP variant with options. It is likely that these two values could also be implied from the Network Appearance, and the Network Appearance could be derived from these two settings. This too requires some more thought. */ if (rlen) { fixme(("Need to handle qor\n")); qor = ((typeof(qor)) mp->b_wptr)++; qor->sls_range = 0xff; qor->mp_range = 0x03; } /* TODO: This is where we whould really be adding SI values. We could then bind to a list of SI values. It would mean that routing keys and the MTP bind operation would need to take a list of MTP addresses. */ if (plen) { fixme(("Need to handle pro\n")); pro = ((typeof(pro)) mp->b_wptr)++; pro[0] = rk->si; } putnext(dp->rq, mp); return (0); enobufs: return (-ENOBUFS); } /* * N_BIND_ACK 17 - NS User bound to network address * --------------------------------------------------------- */ STATIC INLINE int mtpu_bind_ack(queue_t * q) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_bind_ack_t *p; m3ua_addr_t *add, *ss = &sp->src; uint32_t *pro; size_t add_len = ss ? sizeof(*ss) + ss->alen : 0; size_t pro_len = sizeof(uint32_t); size_t msg_len = sizeof(*p) + add_len + pro_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 = N_BIND_ACK; p->ADDR_length = add_len; p->ADDR_offset = add_len ? sizeof(*p) : 0; p->CONIND_number = sp->conind; p->TOKEN_value = (ulong) sp->rq; p->PROTOID_length = pro_len; p->PROTOID_offset = pro_len ? sizeof(*p) + add_len : 0; if (add_len) { add = (typeof(add)) mp->b_wptr; bcopy(ss, add, sizeof(*ss) + ss->alen); mp->b_wptr += add_len; } if (pro_len) { pro = ((typeof(pro)) mp->b_wptr)++; *pro = 3; } sp->i_state = NS_IDLE; putnext(sp->rq, mp); return (0); enobufs: err = -ENOBUFS; ptrace(("ERROR: No buffers\n")); goto error; error: return (err); } /* * N_BIND_ACK 17 - NS User bound to network address * ------------------------------------------------------------------------- * Acknowledges a bind request and returns the bound address. Returning the * bound address is necessary in cases where the MTP is expected to * automatically assign some parameters of the bind. */ static int mtpu_bind_ack(queue_t * q, dp_t * dp, as_t * as) { mblk_t *mp; N_bind_ack_t *p; uint32_t *pro; mtp_addr_t *add, *rk = as->rk; size_t alen = sizeof(*rk); size_t plen = sizeof(uint32_t); size_t mlen = sizeof(*p) + alen; if (!(mp = ua_allocb(q, mlen, BPRI_MED))) goto enobufs; mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_BIND_ACK; p->ADDR_length = alen; p->ADDR_offset = alen ? sizeof(*p) : 0; p->CONIND_number = 0; p->TOKEN_value = (ulong) RD(q); p->PROTOID_length = 0; p->PROTOID_offset = 0; if (alen) { add = ((typeof(add)) mp->b_wptr)++; add->ni = rk->ni; add->si = rk->si; add->pc = rk->pc; } if (plen) { pro = ((typeof(pro)) mp->b_wptr)++; pro[0] = rk->si; } dp->state = NS_IDLE; putnext(dp->rq, mp); return (0); enobufs: return (-ENOBUFS); } /* * N_ERROR_ACK 18 - Error Acknowledgement * --------------------------------------------------------- */ STATIC int mtpu_error_ack(queue_t * q, int prim, int err) { m3ua_t *sp = M3UA_PRIV(q); mblk_t *mp; N_error_ack_t *p; const size_t msg_len = sizeof(*p); switch (err) { case -EBUSY: case -EAGAIN: case -ENOMEM: case -ENOBUFS: case 0: ptrace(("ERROR: No error condition\n")); return (err); } 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 = N_ERROR_ACK; p->ERROR_prim = prim; p->NPI_error = err < 0 ? NSYSERR : err; p->UNIX_error = err < 0 ? -err : 0; switch (sp->i_state) { case NS_WACK_OPTREQ: case NS_WACK_UREQ: case NS_WCON_CREQ: sp->i_state = NS_IDLE; break; case NS_WCON_RREQ: sp->i_state = NS_DATA_XFER; break; case NS_WACK_BREQ: sp->i_state = NS_UNBND; break; case NS_WACK_DREQ6: sp->i_state = NS_WCON_CREQ; break; case NS_WACK_DREQ9: sp->i_state = NS_DATA_XFER; break; case NS_WACK_DREQ10: sp->i_state = NS_WCON_RREQ; break; case NS_WACK_DREQ11: sp->i_state = NS_WRES_RIND; break; /* * Note: if we are not in a WACK state we simply do * not change state. This occurs normally when we * send NOUTSTATE or NNOTSUPPORT or are responding to * an N_OPTMGMT_REQ in other than the NS_IDLE state. */ } putnext(sp->rq, mp); return (0); enobufs: err = -ENOBUFS; ptrace(("ERROR: No buffers\n")); goto error; error: return (err); } /* * N_ERROR_ACK 18 - Error Acknowledgement * ------------------------------------------------------------------------- * Provides negative acknowledgement of an operation (in our case only * N_OPTMGMT_REQ, N_BIND_REQ or N_UNBIND_REQ) and handles the state changes * of the inteface. */ static int mtpu_error_ack(queue_t * q, int prim, int err) { dp_t *dp = (dp_t *) q->q_ptr; mblk_t *mp; N_error_ack_t *p; size_t mlen = sizeof(*p); switch (err) { case -EBUSY: case -EAGAIN: case -ENOMEM: case -ENOBUFS: rare(); return (err); case 0: never(); return (err); } if (!(mp = ua_allocb(mlen, BPRI_MED))) goto enobufs; mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_ERROR_ACK; p->ERROR_prim = prim; p->NPI_error = err < 0 ? NSYSERR : err; p->UNIX_error = err < 0 ? -err : 0; switch (dp->state) { case NS_WACK_OPTREQ: case NS_WACK_UREQ: dp->state = NS_IDLE; break; case NS_WACK_BREQ: dp->state = NS_UNBND; break; /* * NOTE: if we are not in a WACK state we simply do * not change state. This occurs normally when we * send NOUTSTATE or NNOTSUPPORT or are responding to * an N_OPTMGMT_REQ in other than the NS_IDLE state. */ } qreply(q, mp); return (0); enobufs: return (-ENOBUFS); } /* * N_OK_ACK 19 - Success Acknowledgement * --------------------------------------------------------- */ STATIC int mtpu_ok_ack(queue_t * q, ulong prim, ulong seq, ulong tok) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_ok_ack_t *p; const size_t msg_len = sizeof(*p); (void) seq; (void) tok; 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 = N_OK_ACK; p->CORRECT_prim = prim; switch (sp->i_state) { case NS_WACK_OPTREQ: sp->i_state = NS_IDLE; break; case NS_WACK_RRES: sp->i_state = NS_DATA_XFER; break; case NS_WACK_UREQ: sp->i_state = NS_UNBND; break; case NS_WACK_DREQ6: case NS_WACK_DREQ9: case NS_WACK_DREQ10: case NS_WACK_DREQ11: sp->i_state = NS_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 N_OPTMGMT_REQ in other than * the NS_IDLE state. */ } putnext(sp->rq, mp); return (0); enobufs: err = -ENOBUFS; ptrace(("ERROR: No buffers\n")); goto error; error: return (err); } /* * N_OK_ACK 19 - Success Acknowledgement * ------------------------------------------------------------------------- * Provides positive acknowledgement of an operation (in our case only * N_OPTMGMT_REQ, N_UNBIND_REQ) and handles the state changes of the * interface. */ static int mtpu_ok_ack(queue_t * q, ulong prim) { dp_t *dp = (dp_t *) q->q_ptr; mblk_t *mp; N_ok_ack_t *p; size_t mlen = sizeof(*p); if (!(mp = ua_allocb(q, mlen, BPRI_MED))) goto enobufs; mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_OK_ACK; p->CORRECT_prim = prim; switch (dp->state) { case NS_WACK_OPTREQ: dp->state = NS_IDLE; break; case NS_WACK_UREQ: dp->state = NS_UNBND; break; /* * NOTE: if we are not in a WACK state, we simply do * no change state. This occurs normally when we are * responding to an N_OPTMGMT_REQ in other than the * NS_IDLE state. */ } qreply(q, mp); return (0); enobufs: return (-ENOBUFS); } /* * N_UNITDATA_IND 20 - Connection-less data receive indication * --------------------------------------------------------------- */ STATIC INLINE int mtpu_unitdata_ind(queue_t * q, m3ua_addr_t * src, m3ua_addr_t * dst, mblk_t * dp) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_unitdata_ind_t *p; size_t src_len = src ? sizeof(*src) + src->alen : 0; size_t dst_len = dst ? sizeof(*dst) + dst->alen : 0; size_t msg_len = sizeof(*p) + PAD4(src_len) + PAD4(dst_len); if ((1 << sp->i_state) & ~(NSF_IDLE)) goto efault; if (!canputnext(sp->rq)) 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 = N_UNITDATA_IND; p->SRC_length = src_len; p->SRC_offset = src_len ? sizeof(*p) : 0; p->DEST_length = dst_len;; p->DEST_offset = dst_len ? sizeof(*p) + PAD4(src_len) : 0; p->ERROR_type = 0; if (src_len) { bcopy(src, mp->b_wptr, src_len); mp->b_wptr += PAD4(src_len); } if (dst_len) { bcopy(dst, mp->b_wptr, dst_len); mp->b_wptr += PAD4(dst_len); } putnext(sp->rq, 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); } /* * N_UDERROR_IND 21 - UNITDATA Error Indication * --------------------------------------------------------- * NOTE:- We might want to do a special N-NOTICE primitive here because the * N_UDERROR_IND primitive does not have that much information about the * returned message. Or, if we attach the M_DATA block, the use might be * able to gleen more information from the message itself. Another approach * is to stuff some extra information at the end of the primitive. */ STATIC INLINE int mtpu_uderror_ind(queue_t * q, uint error, m3ua_addr_t * dst, mblk_t * dp) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_uderror_ind_t *p; size_t dst_len = dst ? sizeof(*dst) + dst->alen : 0; size_t msg_len = sizeof(*p) + PAD4(dst_len); if ((1 << sp->i_state) & ~(NSF_IDLE)) goto efault; if (!canputnext(sp->rq)) 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 = N_UNITDATA_IND; p->DEST_length = dst_len;; p->DEST_offset = dst_len ? sizeof(*p) : 0; p->RESERVED_field = 0; p->ERROR_type = error; if (dst_len) { bcopy(dst, mp->b_wptr, dst_len); mp->b_wptr += PAD4(dst_len); } putnext(sp->rq, 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); } /* * N_DATACK_IND 24 - Data acknowledgement indication * --------------------------------------------------------- */ STATIC INLINE int mtpu_datack_ind(queue_t * q) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_datack_ind_t *p; N_qos_sel_data_mtp_t *qos; const size_t qos_len = sizeof(*qos); const size_t msg_len = sizeof(*p) + qos_len; if ((1 << sp->i_state) & ~(NSF_DATA_XFER)) goto efault; if (!canputnext(sp->rq)) 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 = N_DATACK_IND; qos = ((typeof(qos)) mp->b_wptr)++; qos->n_qos_type = N_QOS_SEL_DATA_MTP; qos->sls = sp->sls; /* FIXME */ qos->mp = sp->mp; /* FIXME */ fixme(("Get the SLS and MP from the arguments to this function\n" "Actually, it may be worthwhile not delivering QOS\n")); mp->b_cont = dp; putnext(sp->rq, 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); } /* * N_RESET_IND 26 - Incoming NC reset request indication * ------------------------------------------------------------- */ STATIC INLINE int mtpu_reset_ind(queue_t * q, ulong orig, ulong reason, mblk_t * cp) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_reset_ind_t *p; const size_t msg_len = sizeof(*p); if ((1 << sp->i_state) & ~(NSF_DATA_XFER)) goto efault; if (!canputnext(sp->rq)) 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 = N_RESET_IND; p->RESET_orig = orig; p->RESET_reason = reason; bufq_queue(&sp->conq, cp); sp->i_state = NS_WRES_RIND; putnext(sp->rq, 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); } /* * N_RESET_CON 27 - Reset processing complete * --------------------------------------------------------- */ STATIC INLINE int mtpu_reset_con(queue_t * q) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_reset_con_t *p; const size_t msg_len = sizeof(*p); if ((1 << sp->i_state) & ~(NSF_WCON_RREQ)) goto efault; if (!canputnext(sp->rq)) 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 = N_RESET_CON; sp->i_state = NS_DATA_XFER; putnext(sp->rq, 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); } #if 0 /* * N_RECOVER_IND 29 - NC Recovery indication * --------------------------------------------------------- */ STATIC INLINE int mtpu_recover_ind(queue_t * q) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_recover_ind_t *p; const size_t msg_len = sizeof(*p); if (!canputnext(sp->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 = N_RECOVER_IND; sp->i_state = NS_DATA_XFER; putnext(sp->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); } /* * N_RETRIEVE_IND 32 - NC Retrieval indication * --------------------------------------------------------- */ STATIC int mtpu_retrieve_ind(queue_t * q, mblk_t * dp) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_retrieve_ind_t *p; const size_t msg_len = sizeof(*p); if ((1 << sp->i_state) & ~(NSF_IDLE)) goto efault; if (!canputnext(sp->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 = N_RETREIVE_IND; mp->b_cont = dp; sp->i_state = NS_IDLE; putnext(sp->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 from wrong state\n")); goto error; error: return (err); } /* * N_RETRIEVE_CON 33 - NC Retrieval complete confirmation * ----------------------------------------------------------- */ STATIC int mtpu_retrieve_con(queue_t * q) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *mp; N_retrieve_con_t *p; const size_t msg_len = sizeof(*p); if ((1 << sp->i_state) & ~(NSF_IDLE)) goto efault; if (!canputnext(sp->q)) goto ebusy; if ((mp = ua_allocb(q, msg_len, BPRI_MED))) mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_RETRIEVE_CON; sp->i_state = NS_IDLE; putnext(sp->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: confirmation from wrong state\n")); goto error; error: return (err); } #endif /* * MTP User --> M3UA Primitives * ------------------------------------------------------------------------- * This is the MTPU upper write routine. This takes MTP Primitives and * converts them to M3UA M_DATA and M_CTL messages, if required. If the * provider is a local SG, then the primitives are forwarded as is. * * These are primitive sent from a local MTP user to the M3UA layer on the * upper mux stream. This is part of the Adaptation Layer at an ASP * (Application Server Process). Each message is converted from an MTP request * or response primitive to an M3UA message. These functions are pure * translation only. When they return zero, they return the MTP primtive * translated in `mpp'. When they return non-zero, they return an error and * `mpp' is untouched. */ /* * N_CONN_REQ 0 - NC request * --------------------------------------------------------------- * * 4.2.1.1 N_CONN_REQ - Network Connection Request * * This primtive requests that the NS provider make a network connection to * the specified destination. The format is one M_PROTO message block * followed by one ore more M_DATA blocks for the NS user data transfer. The * specification of NS user data is optional. The NS user can send any * integral number of octets of data within the range supported by the NS * provider (see N_INFO_ACK). If the user does not specify QOS parameter * values, the default values (specified via N_OPTMGMT_REQ) are used by the * NS provider. * * TODO: If a stream is bound as a listener stream [conind > 0], it will not * be able to initiate connect requests. If the NS user attempts to send an * N_CONN_REQ primitive down this stream, an N_ERROR_ACK message will be sent * to the NS user by the NS provider with an error value of NACCESS. */ STATIC int mtpu_conn_req(queue_t * q, mblk_t * mp) { m3ua_t *sp = M3UA_PRIV(q); m3ua_addr_t *a; int err = -EFAULT; size_t mlen = mp->b_wptr - mp->b_rptr; N_conn_req_t *p = ((typeof(p)) mp->b_rptr); N_qos_sel_conn_mtp_t *qos = (typeof(qos)) (mp->b_rptr + p->QOS_offset); if (sp->i_state != NS_IDLE) goto noutstate sp->i_state = NS_WCON_CREQ; if (mlen < sizeof(*p)) goto einval; if (p->QOS_length && mlen < p->QOS_offset + p->QOS_length) goto nbadopt2; if (p->QOS_length && qos->n_qos_type != N_QOS_SEL_CONN_MTP) goto nbadqostype; if (p->QOS_length && p->QOS_length != sizeof(*qos)) goto nbadopt; a = (m3ua_addr_t *) (mp->b_rptr + p->DEST_offset); if (!p->DEST_length) goto nnoaddr; if ((mlen < p->DEST_offset + p->DEST_length) || (p->DEST_length < sizeof(m3ua_addr_t) + a->alen) || a->ssn) goto nbadaddr; if (sp->cred.cr_uid == 0 || a->ssn) goto naccess; fixme(("select a source local reference number\n")); if (p->QOS_length) sp->pclass = qos->protocol_class; sp->flags &= ~(M3UA_FLAG_REC_CONF_OPT | M3UA_FLAG_EX_DATA_OPT); if (p->CONN_flags & REC_CONF_OPT) sp->flags |= M3UA_FLAG_REC_CONF_OPT; if (p->CONN_flags & EX_DATA_OPT) sp->flags |= M3UA_FLAG_EX_DATA_OPT; if ((err = m3ua_conn_req(q, a, mp->b_cont))) goto badconn; mp->b_cont = NULL; /* absorbed mp->b_cont */ return (0); badconn: err = err; ptrace(("ERROR: couldn't connect\n")); goto error; naccess: err = NACCESS; ptrace(("ERROR: no permission for requested address\n")); goto error; nbadaddr: err = NBADADDR; ptrace(("ERROR: address is unusable\n")); goto error; nnoaddr: err = NNOADDR; ptrace(("ERROR: no destination address\n")); goto error; nbadopt: err = NBADOPT; ptrace(("ERROR: options are unusable\n")); goto error; nbadqostype: err = NBADQOSTYPE; ptrace(("ERROR: QOS structure type not supported\n")); goto error; nbadopt2: err = NBADOPT; ptrace(("ERROR: options are unusable\n")); goto error; einval: err = -EINVAL; ptrace(("ERROR: invalid message format\n")); goto error; noutstate: err = NOUTSTATE; ptrace(("ERROR: would place interface out of state\n")); goto error; error: return mtpu_error_ack(q, N_CONN_REQ, err); } /* * N_CONN_RES 1 - Accept previous connection indication * --------------------------------------------------------------- * We don't support connection indications or connection responses for * connectionless protocols. */ STATIC int mtpu_conn_res(queue_t * q, mblk_t * mp) { (void) q; (void) mp; return mtpu_error_ack(q, N_CONN_RES, NNOTSUPPORT); } /* * N_DISCON_REQ 2 - NC disconnection request * --------------------------------------------------------- */ STATIC int mtpu_discon_req(queue_t * q, mblk_t * mp) { m3ua_t *sp = M3UA_PRIV(q); int err; mblk_t *cp = NULL; size_t mlen = mp->b_wptr - mp->b_rptr; N_discon_req_t *p = (typeof(p)) mp->b_rptr; switch (sp->i_state) { case NS_WCON_CREQ: sp->i_state = NS_WACK_DREQ6; break; case NS_DATA_XFER: sp->i_state = NS_WACK_DREQ9; break; case NS_WCON_RREQ: sp->i_state = NS_WACK_DREQ10; break; case NS_WRES_RIND: sp->i_state = NS_WACK_DREQ11; break; default: goto noutstate; } if (mlen < sizeof(*p)) goto einval; if (p->RES_length) goto nbadaddr; if (p->SEQ_number) goto nbadseq; /* * XXX: What to do with the disconnect reason? Nothing? */ if ((err = m3ua_discon_req(q, cp))) goto baddisconn; return mtpu_ok_ack(q, N_DISCON_REQ, 0, 0); baddiscon: err = err; ptrace(("ERROR: couldn't disconnect\n")); goto error; nbadseq: err = NBADSEQ; ptrace(("ERROR: connection ind reference is invalid\n")); goto error; nbadaddr: err = NBADADDR; ptrace(("ERROR: responding addres invalid\n")); goto error; einval: err = -EINVAL; ptrace(("ERROR: invalid primitive format\n")); goto error; noutstate: err = NOUTSTATE; ptrace(("ERROR: would place interface out of state\n")); goto error; error: return mtpu_error_ack(q, N_DISCON_REQ, err); } /* * N_DATA_REQ 3 - Connection-Mode data transfer request * -------------------------------------------------------------- */ STATIC int mtpu_error_reply(queue_t * q, int err) { m3ua_t *sp = M3UA_PRIV(q); mblk_t *mp; switch (err) { case -EBUSY: case -EAGAIN: case -ENOMEM: case -ENOBUFS: case 0: case 1: case 2: ptrace(("ERROR: No error condition\n")); 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; putnext(sp->rq, mp); return (0); } return (-ENOBUFS); } STATIC INLINE int mtpu_write(queue_t * q, mblk_t * mp) { m3ua_t *sp = M3UA_PRIV(q); int err; if (sp->i_state == NS_IDLE) goto discard; if ((1 << sp->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND)) goto eproto; if ((err = m3ua_data_req(q, 0, 0, 0, mp))) goto error; return (QR_ABSORBED); /* absorbed mp */ eproto: err = -EPROTO; ptrace(("ERROR: would place interface out of state\n")); goto error; discard: err = 0; ptrace(("ERROR: ignoring in idle state\n")); return (err); error: return mtpu_error_reply(q, err); } STATIC INLINE int mtpu_data_req(queue_t * q, mblk_t * mp) { m3ua_t *sp = M3UA_PRIV(q); int err; N_qos_sel_data_mtp_t *qos = NULL; size_t mlen = mp->b_wptr - mp->b_rptr; N_data_req_t *p = (typeof(p)) mp->b_rptr; uint exp, more, rcpt; if (sp->i_state == NS_IDLE) goto discard; if (mlen < sizeof(*p)) goto einval; if (mlen >= sizeof(*p) + sizeof(*qos)) { qos = (typeof(qos)) (p + 1); if (qos->n_qos_type != N_QOS_SEL_DATA_MTP) qos = NULL; } if ((1 << sp->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND)) goto eproto; exp = 0; /* FIXME */ more = 0; /* FIXME */ rcpt = 0; /* FIXME */ fixme(("We need to adjust the data req function to take sls and mp\n")); if ((err = m3ua_data_req(q, exp, more, rcpt, mp->b_cont))) goto error; return (QR_ABSORBED); /* absorbed mp->b_cont */ 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(("ERROR: ignore in idle state\n")); return (0); error: return mtpu_error_reply(q, err); } /* * N_EXDATA_REQ 4 - Expedited data request * --------------------------------------------------------- */ STATIC INLINE int mtpu_exdata_req(queue_t * q, mblk_t * mp) { m3ua_t *sp = M3UA_PRIV(q); int err; N_qos_sel_data_mtp_t *qos = NULL; size_t mlen = mp->b_wptr - mp->b_rptr; N_exdata_req_t *p = (typeof(p)) mp->b_rptr; uint exp, more, rcpt; if (sp->i_state == NS_IDLE) goto discard; if (mlen < sizeof(*p)) goto einval; if (mlen >= sizeof(*p) + sizeof(*qos)) { qos = (typeof(qos)) (p + 1); if (qos->n_qos_type != N_QOS_SEL_DATA_MTP) qos = NULL; } if ((1 << sp->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND)) goto eproto; exp = 0; /* FIXME */ more = 0; /* FIXME */ rcpt = 0; /* FIXME */ fixme(("We need to adjust the data req function to take sls and mp\n")); if ((err = m3ua_data_req(q, exp, more, rcpt, mp->b_cont))) goto error; return (QR_ABSORBED); /* absorbed mp->b_cont */ 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(("ERROR: ignore in idle state\n")); return (0); error: return mtpu_error_reply(q, err); } /* * N_INFO_REQ 5 - Information request * --------------------------------------------------------- */ STATIC int mtpu_info_req(queue_t * q, mblk_t * mp) { (void) mp; return mtpu_info_ack(q); } /* * N_INFO_REQ * ------------------------------------------------------------------------- * If we get this request we feedback local information. When we receive an * ASPAC ACK and we are implementing draft-bidulock-sigtran-sginfo-00.me, this * information will be updated whenever the AS is activated (i.e, when the * interface is in the NS_IDLE state. */ static int mtpu_info_req(queue_t * q, mblk_t * mp) { (void) mp; return mtpu_info_ack(q); } /* * N_BIND_REQ 6 - Bind a NS user to network address * ---------------------------------------------------------- */ STATIC int mtpu_bind_req(queue_t * q, mblk_t * mp) { m3ua_t *sp = M3UA_PRIV(q); int err; size_t mlen = mp->b_wptr - mp->b_rptr; N_bind_req_t *p = (typeof(p)) mp->b_rptr; m3ua_addr_t *a; if (sp->i_state != NS_UNBND) goto noutstate; sp->i_state = NS_WACK_BREQ; if (mlen < sizeof(*p)) goto einval; a = (m3ua_addr_t *) (mp->b_rptr + p->ADDR_offset); if ((mlen < p->ADDR_offset + p->ADDR_length) || (p->ADDR_length < sizeof(m3ua_addr_t) + a->alen) && a->ssn) goto nbadaddr; fixme(("Deal with wildcards\n")); fixme(("Deal with default binds\n")); fixme(("Deal with slr assignment\n")); if (0) goto nnoaddr; if (sp->cred.cr_uid != 0 && !a->ssn) goto naccess; if ((err = m3ua_bind_req(q, a, p->CONIND_number))) goto error; return mtpu_bind_ack(q); naccess: err = NACCESS; ptrace(("ERROR: no permission for requested address\n")); goto error; nnoaddr: err = NNOADDR; ptrace(("ERROR: could not allocate address\n")); goto error; nbadaddr: err = NBADADDR; ptrace(("ERROR: address is invalid\n")); goto error; einval: err = -EINVAL; ptrace(("ERROR: invalid primitive format\n")); goto error; noutstate: err = NOUTSTATE; ptrace(("ERROR: would place interface out of state\n")); goto error; error: return mtpu_error_ack(q, N_BIND_REQ, err); } /* * N_BIND_REQ * ------------------------------------------------------------------------- * Unfortunately an N_BIND_REQ does not map into a single M3UA message. When * we bind we need to do three things: register (if required), activate, and * audit. The question is when to return the bind_ack. Probably after the * ASPAC ACK. So we need to (a) launch a REG REQ, and mark that we are waiting * for REQ RSP. On successful REG RSP, launch ASPAC and wait for ASPAC ACK. * If we get an error we send ERROR_ACK. If we get ASPAC ACK, we send * BIND_ACK. Then we launch DAUD and handle the responses as normal. All the * while we wait in NS_WACK_BREQ. */ static int mtpu_bind_req(queue_t * q, mblk_t * mp) { int err = 0; mtp_addr_t *rk; gp_t *gp; as_t *as; dp_t *dp = ((dp_t *) q->q_ptr); /* device structure */ xp_t *xp = dp->xp; sp_t *sp = dp->sp; size_t mlen = mp->b_wptr - mp->b_rptr; N_bind_req_t *p = (typeof(p)) mp->b_rptr; mtp_addr_t *addr; if (dp->state != NS_UNBND) { err = NOUTSTATE; ptrace(("ERROR: would place i/f out of state\n")); goto mtpu_error_ack; } dp->state = NS_WACK_BREQ; if (mlen < sizeof(*p)) { err = -EINVAL; ptrace(("ERROR: M_PROTO is too small\n")); goto mtpu_error_ack; } if (mlen < p->ADDR_offset + p->ADDR_length || p->ADDR_length != sizeof(*addr)) { err = -EINVAL; ptrace(("ERROR: invalid primitive format\n")); goto mtpu_error_ack; } addr = (struct mtp_addr *) (mp->b_rptr + p->ADDR_offset); if (addr->si <= 2) { err = NNOADDR; ptrace(("ERROR: could not allocate address\n")); goto mtpu_error_ack; } if ((addr->pc & 0xff000000)) { err = NBADADDR; ptrace(("ERROR: address is invalid\n")); goto mtpu_error_ack; } /* * First we go looking for an AS with the same routing key as this. */ for (as = sp->as; as; as = as->sp.next) if ((rk = as->rk) && rk->ni == addr->ni && rk->si == addr->si && rk->pc == addr->pc) break; if (!as) { err = NBADADDR; ptrace(("ERROR: address unreachable\n")); goto mtpu_error_ack; } if (as->pp) { /* no SS7-U attached */ err = NNOADDR; ptrace(("ERROR: AS is in use\n")); goto mtpu_error_ack; } if ((1 << as->state) & (ASM_DOWN_STATES | ASM_UREG_STATES)) { err = NNOADDR; ptrace(("ERROR: AS is down or unregistered\n")); goto mtpu_error_ack; } if ((1 << as->state) & (ASF_BLOCKED)) { err = NACCESS; ptrace(("ERROR: AS is management blocked\n")); goto mtpu_error_ack; } if (!(gp = ua_gp_alloc())) { err = -ENOMEM; ptrace(("ERROR: couldn't allocate cache\n")); goto mtpu_error_ack; } /* fill out GP */ gp->rc = 0; gp->flags = AS_STATIC; gp->state = AS_DOWN; /* link to AS */ gp->as.as = as; if ((gp->as.next = as->gp)) gp->as.next->as.prev = &gp->as.next; gp->as.prev = &as->gp; as->gp = gp; /* link to XP */ gp->xp.xp = xp; if ((gp->xp.next = xp->gp)) gp->xp.next->xp.prev = &gp->xp.next; gp->xp.prev = &xp->gp; xp->gp = gp; as->xpat_count++; as->xpdn_count++; xp->asat_count++; xp->asdn_count++; switch (as->state) { case AS_INACTIVE: case AS_PENDING: if ((err = as_active_req(q, as))) { if (err < 0) return (err); else break; } break; case AS_WACK_ASPAC: if (as->tmode == AS_TMODE_OVERRIDE) { if ((err = asp_override(q, as)) < 0) { if (err < 0) return (err); else break; } break; } as->xpar_count++; break; case AS_WACK_ASPIA: if ((err = asp_inactive_ack(q, as))) { if (err < 0) return (err); else break; } if ((err = as_active_req(q, as))) { if (err < 0) return (err); else break; } break; case AS_ACTIVE: case AS_WACK_HBEAT: if (as->tmode == AS_TMODE_OVERRIDE) if ((err = asp_override(q, as))) { if (err < 0) return (err); else break; } if ((err = mtpu_bind_ack(q, dp, as))) return (err); /* delayed notification for `n' asps */ if (as->xpac_count == as->mina_count - 1) if ((err = ua_ntfy_as_change(q, as, UA_STATUS_AS_ACTIVE))) return (err); as->xpia_count--; as->xpac_count++; break; default: never(); err = -EFAULT; ptrace(("SWERR: AS is in state %d\n", as->state)); break; } if (!err) { as->pp = dp; dp->as = as; as->xpat_count++; return (0); } mtpu_error_ack: seldom(); return mtpu_error_ack(q, N_BIND_REQ, err); } /* * N_UNBIND_REQ 7 - Unbind NS user from network address * ------------------------------------------------------------ */ STATIC int mtpu_unbind_req(queue_t * q, mblk_t * mp) { m3ua_t *sp = M3UA_PRIV(q); int err; N_unbind_req_t *p = (typeof(p)) mp->b_rptr; (void) p; if (sp->i_state != NS_IDLE) goto noutstate; sp->i_state = NS_WACK_UREQ; if ((err = m3ua_unbind_req(q))) goto error; return mtpu_ok_ack(q, N_UNBIND_REQ, 0, 0); noutstate: err = NOUTSTATE; ptrace(("ERROR: would place interface out of state\n")); goto error; error: return mtpu_error_ack(q, N_UNBIND_REQ, err); } /* * N_UNBIND_REQ * ------------------------------------------------------------------------- * Check the interface status. If we are NS_IDLE, then we want to do two * things: ASPIA and then DEREG REQ (if required). All the while we are in * state NS_WACK_UREQ. */ static int mtpu_unbind_req(queue_t * q, mblk_t * mp) { int err; as_t *as; dp_t *dp = ((dp_t *) q->q_ptr); /* device structure */ N_unbind_req_t *p = (N_unbind_req_t *) mp->b_rptr; (void) p; if (!(as = dp->as)) { err = NOUTSTATE; ptrace(("ERROR: would place i/f out of state\n")); goto mtpu_error_ack; } if (dp->state != NS_IDLE) { err = -EFAULT; ptrace(("ERROR: no attached AS-U\n")); goto mtpu_error_ack; } dp->state = NS_WACK_UREQ; if (as->xpac_count == 1) if ((err = as_inactive_req(q, as)) < 0) goto mtpu_error_ack; as->pp = NULL; dp->as = NULL; as->xpat_count--; as->xpac_count--; return mtpu_ok_ack(q, N_UNBIND_REQ); mtpu_error_ack: seldom(); return mtpu_error_ack(q, N_UNBIND_REQ, err); } /* * N_UNITDATA_REQ * ------------------------------------------------------------------------- */ static int mtpu_error_reply(queue_t * q, int err) { dp_t *dp = (dp_t *) q->q_ptr; mblk_t *mp; size_t mlen = 2; switch (err) { case -EBUSY: case -EAGAIN: case -ENOMEM: case -ENOBUFS: rare(); return (err); case 0: case 1: case 2: never(); return (err); } if ((mp = ua_allocb(mlen, BPRI_HI))) { mp->b_datap->db_type = M_ERROR; *(mp->b_wptr++) = err < 0 ? -err : err; *(mp->b_wptr++) = err < 0 ? -err : err; putnext(dp->rq, mp); return (0); } return (-ENOBUFS); } static int mtpu_unitdata_req(queue_t * q, mblk_t * pdu) { int err; dp_t *dp = ((dp_t *) q->q_ptr); /* device structure */ as_t *as = dp->as; N_qos_sel_data_mtp_t *qos = NULL; size_t plen = pdu->b_wptr - pdu->b_rptr; N_unitdata_req_t *p = (typeof(p)) pdu->b_rptr; struct mtp_addr *a; if (dp->state != NS_IDLE) { err = -EPROTO; ptrace(("ERROR: would place i/f out of state\n")); goto mtpu_error_ack; } if (plen < sizeof(*p)) { err = -EINVAL; ptrace(("ERROR: invalid primitive format\n")); goto mtpu_error_ack; } if (plen < p->DEST_offset + p->DEST_length || p->DEST_length != sizeof(*a)) { err = -EINVAL; ptrace(("ERROR: invalid primitive format\n")); goto mtpu_error_ack; } if (plen >= sizeof(*p) + sizeof(*a) + sizeof(*qos)) { qos = (typeof(qos)) (pdu->b_rptr + sizeof(*p) + p->DEST_offset + p->DEST_length); if (qos->n_qos_type != N_QOS_SEL_DATA_MTP) qos = NULL; } { mblk_t *mp; mtp_addr_t *dst = (typeof(dst)) (pdu->b_rptr + p->DEST_offset); size_t mlen = 10 * sizeof(uint32_t); size_t dlen = msgdsize(pdu->b_cont); if ((mp = ua_allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; *((uint32_t *) mp->b_wptr)++ = M3UA_XFER_DATA; *((uint32_t *) mp->b_wptr)++ = htonl(mlen + UA_PAD4(dlen)); fixme(("Actually pad the data\n")); *((uint32_t *) mp->b_wptr)++ = UA_PARM_RC; *((uint32_t *) mp->b_wptr)++ = htonl(as->rc); *((uint32_t *) mp->b_wptr)++ = UA_PARM_CORR_ID; *((uint32_t *) mp->b_wptr)++ = htonl(as->cid++); *((uint32_t *) mp->b_wptr)++ = htonl(0); /* XXX */ *((uint32_t *) mp->b_wptr)++ = htonl(dst->pc); *(mp->b_wptr)++ = dst->si; *(mp->b_wptr)++ = dst->ni; *(mp->b_wptr)++ = qos->mp; *(mp->b_wptr)++ = qos->sls; mp->b_cont = pdu->b_cont; pdu->b_cont = NULL; return (0); } return (-ENOBUFS); } mtpu_error_ack: seldom(); return mtpu_error_reply(q, err); } /* * N_UNITDATA_REQ 8 - Unitdata request * --------------------------------------------------------- */ STATIC int mtpu_uderror_reply(queue_t * q, mblk_t * mp, int etype) { N_uderror_ind_t *p = (typeof(p)) mp->b_rptr; switch (etype) { case -EBUSY: case -EAGAIN: case -ENOMEM: case -ENOBUFS: seldom(); return (etype); case 0: case 1: case 2: never(); return (etype); } if (etype > 0) { p->PRIM_type = N_UDERROR_IND; p->ERROR_type = etype; } else { mp->b_datap->db_type = M_ERROR; mp->b_wptr = mp->b_rptr; *(mp->b_wptr)++ = -etype; *(mp->b_wptr)++ = -etype; } qreply(q, mp); return (QR_ABSORBED); } STATIC INLINE int mtpu_unitdata_req(queue_t * q, mblk_t * mp) { m3ua_t *sp = M3UA_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 N_unitdata_req_t *p = (typeof(p)) mp->b_rptr; N_qos_sel_data_mtp_t *qos; m3ua_addr_t *dst; ulong sls, mp; if (dlen == 0 || dlen > sp->mtu) goto eproto; if (sp->i_state != NS_IDLE) goto eproto2; if (mlen < sizeof(*p)) goto eproto3; dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset); qos = NULL; if ((mlen < p->DEST_offset + p->DEST_length) || (!p->DEST_offset) || (p->DEST_offset < sizeof(*p)) || (p->DEST_length < sizeof(*dst)) || (p->DEST_length < sizeof(*dst) + dst->alen)) goto eproto4; if (sp->cred.cr_uid != 0 && !dst->ssn) goto eproto5; if (mlen >= p->DEST_offset + p->DEST_length + PAD4(dst->alen) + sizeof(*qos)) { qos = (typeof(qos)) (mp->b_rptr + p->DEST_offset + p->DEST_length); if (qos->n_qos_type != N_QOS_SEL_DATA_MTP) qos = NULL; } sls = qos ? qos->sls : sp->sls; mp = qos ? qos->mp : sp->mp; fixme(("We need to adjust the data req function to take sls and mp\n")); if ((err = m3ua_unitdata_req(q, dst, pclass, options, import, seq, pri, mp->b_cont))) goto error; return (QR_TRIMMED); eproto5: err = -EPROTO; ptrace(("ERROR: no permission to send to address\n")); goto error; eproto4: err = -EPROTO; ptrace(("ERROR: bad destnation 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 mtpu_uderror_reply(q, mp, err); } /* * N_OPTMGMT_REQ 9 - Options management request * --------------------------------------------------------- */ STATIC INLINE int mtpu_optmgmt_req(queue_t * q, mblk_t * mp) { m3ua_t *sp = M3UA_PRIV(q); int err; size_t mlen = mp->b_wptr - mp->b_rptr; N_optmgmt_req_t *p = (typeof(p)) mp->b_rptr; N_qos_sel_info_mtp_t *qos = (typeof(qos)) (mp->b_rptr + p->QOS_offset); if (sp->i_state == NS_IDLE) sp->i_state = NS_WACK_OPTREQ; if (mlen < sizeof(*p)) goto einval; if (p->QOS_length && mlen < p->QOS_offset + p->QOS_length) goto nbadopt; if (p->QOS_length && qos->n_qos_type != N_QOS_SEL_INFO_MTP) goto nbadqostype; if (p->QOS_length && p->QOS_length != sizeof(*qos)) goto nbadopt2; if (p->QOS_length) { if (qos->protocol_class != -1UL) sp->pvar = qos->pvar; if (qos->option_flags != -1UL) sp->popt = qos->popt; } if (p->OPTMGMT_flags & DEFAULT_RC_SEL) sp->flags |= M3UA_FLAG_DEFAULT_RC_SEL; else sp->flags &= ~M3UA_FLAG_DEFAULT_RC_SEL; return mtpu_ok_ack(q, N_OPTMGMT_REQ, 0, 0); nbadopt2: err = NBADOPT; ptrace(("ERROR: invalid qos options\n")); goto error; nbadqostype: err = NBADQOSTYPE; ptrace(("ERROR: bad qos structure type\n")); goto error; nbadopt: err = NBADOPT; ptrace(("ERROR: bad qos structure format\n")); goto error; einval: err = -EINVAL; ptrace(("ERROR: bad primitive format\n")); goto error; error: return mtpu_error_ack(q, N_OPTMGMT_REQ, err); } /* * N_OPTMGMT_REQ * ------------------------------------------------------------------------- */ static int mtpu_optmgmt_req(queue_t * q, mblk_t * mp) { int err; dp_t *dp = ((dp_t *) q->q_ptr); /* device structure */ size_t mlen = mp->b_wptr - mp->b_rptr; N_optmgmt_req_t *p; N_qos_sel_info_mtp_t *qos; if (dp->state == NS_IDLE) dp->state = NS_WACK_OPTREQ; if (mlen < sizeof(*p)) { err = -EINVAL; ptrace(("ERROR: invalid primitive format\n")); goto mtpu_error_ack; } p = (typeof(p)) (mp->b_rptr); qos = (typeof(qos)) (mp->b_rptr + p->QOS_offset); if (p->OPTMGMT_flags) { err = NBADFLAG; ptrace(("ERROR: flags in primitive were invalid\n")); goto mtpu_error_ack; } if (p->QOS_length && mlen < p->QOS_offset + p->QOS_length) { err = NBADOPT; ptrace(("ERROR: QOS options were invalid\n")); goto mtpu_error_ack; } if (p->QOS_length && p->QOS_length != sizeof(*qos)) { err = NBADOPT; ptrace(("ERROR: QOS options were invalid\n")); goto mtpu_error_ack; } if (p->QOS_length && qos->n_qos_type != N_QOS_SEL_INFO_MTP) { err = NBADQOSTYPE; ptrace(("ERROR: QOS structure type not supported\n")); goto mtpu_error_ack; } if (p->QOS_length) { if (qos->pvar) { } if (qos->popt) { } return mtpu_ok_ack(q, N_OPTMGMT_REQ); } err = NBADOPT; ptrace(("ERROR: QOS options were invalid\n")); mtpu_error_ack: seldom(); return mtpu_error_ack(q, N_OPTMGMT_REQ, err); } /* * N_DATACK_REQ 23 - Data acknowledgement request * --------------------------------------------------------- */ STATIC INLINE int mtpu_datack_req(queue_t * q, mblk_t * mp) { (void) mp; fixme(("SWERR: Write this function\n")); return mtpu_error_ack(q, N_DATACK_REQ, NNOTSUPPORT); } /* * N_RESET_REQ 25 - NC reset request * --------------------------------------------------------- */ STATIC int mtpu_reset_req(queue_t * q, mblk_t * mp) { m3ua_t *sp = M3UA_PRIV(q); int err; size_t mlen = mp->b_wptr - mp->b_rptr; N_reset_req_t *p = (typeof(p)) mp->b_rptr; if (sp->i_state == NS_IDLE) goto discard; if (sp->i_state != NS_DATA_XFER) goto noutstate; sp->i_state = NS_WCON_RREQ; (void) p; (void) mlen; if ((err = m3ua_reset_req(q))) goto error; return (0); noutstate: err = NOUTSTATE; ptrace(("ERROR: would place interface out of state\n")); goto error; discard: ptrace(("ERROR: ignore in idle state\n")); return (0); error: return mtpu_error_ack(q, N_RESET_REQ, err); } /* * N_RESET_RES 27 - Reset processing accepted * --------------------------------------------------------- */ STATIC INLINE int mtpu_reset_res(queue_t * q, mblk_t * mp) { m3ua_t *sp = M3UA_PRIV(q); int err; size_t mlen = mp->b_wptr - mp->b_rptr; N_reset_res_t *p = (typeof(p)) mp->b_rptr; if (sp->i_state == NS_IDLE) goto discard; if (sp->i_state != NS_WRES_RIND) goto noutstate; sp->i_state = NS_WACK_RRES; (void) p; (void) mlen; if ((err = m3ua_reset_res(q))) goto error; return mtpu_ok_ack(q, N_RESET_RES, 0, 0); noutstate: err = NOUTSTATE; ptrace(("ERROR: would place interface out of state\n")); goto error; discard: ptrace(("ERROR: ignore in idle state\n")); return (0); error: return mtpu_error_ack(q, N_RESET_RES, err); } /* * M_IOCTL Handling * ------------------------------------------------------------------------- * This function is responsible for linking transport and MTP provider streams * under the multiplexor. */ static int mtpu_w_ioctl(queue_t * q, mblk_t * mp) { return lm_w_ioctl(q, mp); } /* * M_DATA Handling * ------------------------------------------------------------------------- * The MTP-User is not permitted to write M_DATA blocks directly. These are * connectionless service only. */ static int mtpu_w_data(queue_t * q, mblk_t * dp) { rare(); return mtpu_error_reply(q, -EOPNOTSUPP); } /* * M_PROTO Handling * ------------------------------------------------------------------------- */ static int mtpu_w_proto(queue_t * q, mblk_t * mp) { int prim; switch ((prim = *((long *) mp->b_rptr))) { case N_CONN_REQ: return mtpu_conn_req(q, mp); case N_CONN_RES: return mtpu_conn_res(q, mp); case N_DISCON_REQ: return mtpu_discon_req(q, mp); case N_DATA_REQ: return mtpu_data_req(q, mp); case N_EXDATA_REQ: return mtpu_exdata_req(q, mp); case N_INFO_REQ: return mtpu_info_req(q, mp); case N_BIND_REQ: return mtpu_bind_req(q, mp); case N_UNBIND_REQ: return mtpu_unbind_req(q, mp); case N_UNITDATA_REQ: return mtpu_unitdata_req(q, mp); case N_OPTMGMT_REQ: return mtpu_optmgmt_req(q, mp); case N_DATACK_REQ: return mtpu_datack_req(q, mp); case N_RESET_REQ: return mtpu_reset_req(q, mp); case N_RESET_RES: return mtpu_reset_res(q, mp); } rare(); return mtpu_error_ack(q, prim, NNOTSUPPORT); } /* * M_PCPROTO Handling * ------------------------------------------------------------------------- */ static int mtpu_w_pcproto(queue_t * q, mblk_t * mp) { return mtpu_w_proto(q, mp); } /* * ------------------------------------------------------------------------- */ static int mtpu_w_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: return mtpu_w_data(q, mp); case M_PROTO: return mtpu_w_proto(q, mp); case M_PCPROTO: return mtpu_w_pcproto(q, mp); case M_IOCTL: return mtpu_w_ioctl(q, mp); case M_FLUSH: return ua_w_flush(q, mp); } rare(); return (-EOPNOTSUPP); } /* * M3UA --> MTP User Primitives * ------------------------------------------------------------------------- * This is the MTPU upper read primitive routines. This takes M3UA M_DATA and * M_CTL blocks and converts them to MTP User primitives. * * These are primitives sent to a local MTP User from the M3UA layer on the * upper mux stream. This is part of the Adaptation Layer at an ASP * (Application Server Process). Each message is converted from an M3UA * message to an MTP indication or confirmation primitive. These function are * pure translation function. When they return zero, they return the M3UA * message translated in `mpp'. When they return negative, they return an * error and `mpp' is untouched. */ /* * MGMT ERR * ------------------------------------------------------------------------ * We receive a copy of error messages if they result in the abort of a bind or * unbind attempt. These messages are turned in to N_ERROR_ACks if we are in a * WACK state. */ static int mtpu_mgmt_err(queue_t * q, mblk_t * pdu) { int err, prim; dp_t *dp = (dp_t *) q->q_ptr; (void) dp; switch (dp->state) { case NS_WACK_BREQ: prim = N_BIND_REQ; err = -EFAULT; ptrace(("ERROR: Aborting bind request\n")); break; case NS_WACK_UREQ: prim = N_UNBIND_REQ; err = -EFAULT; ptrace(("ERROR: Aborting unbind request\n")); break; case NS_WACK_OPTREQ: prim = N_OPTMGMT_REQ; err = -EFAULT; ptrace(("ERROR: Aborting option management request\n")); break; default: rare(); return (-EFAULT); } return mtpu_error_ack(q, prim, err); } /* * MGMT NTFY * ------------------------------------------------------------------------- */ static int mtpu_mgmt_ntfy(queue_t * q, mblk_t * pdu) { (void) q; (void) pdu; ptrace(("Received MGMT NTFY at MTPU RD(q)\n")); /* this is an error */ return (-EFAULT); } /* * ASPS ASPUP Ack * ------------------------------------------------------------------------- */ static int mtpu_asps_aspup_ack(queue_t * q, mblk_t * pdu) { (void) q; (void) pdu; ptrace(("Received ASPUP ACK at MTPU RD(q)\n")); /* this is an error */ return (-EFAULT); } /* * ASPS ASPDN Ack * ------------------------------------------------------------------------- */ static int mtpu_asps_aspdn_ack(queue_t * q, mblk_t * pdu) { (void) q; (void) pdu; ptrace(("Received ASPDN ACK at MTPU RD(q)\n")); /* this is an error */ return (-EFAULT); } /* * ASPS BEAT Req * ------------------------------------------------------------------------- * TODO: If we receive a BEAT, it is because the underlying M3UA layer is * trying to determine when all data has been successfully pushed to the * MTP-User. This is for coordinated changeovers and things. What we should * do is mark the message and put it on the back of the queue (e.g., with the * MSG_DELIM flag). When we receive it again with the MSG_DELIM flag set, we * know that the queue has been cycled and we can reply with a BEAT ack. */ static int mtpu_asps_hbeat_req(queue_t * q, mblk_t * pdu) { (void) q; (void) pdu; ptrace(("Received HBEAT REQ at MTPU RD(q)\n")); return (-EFAULT); } /* * ASPS BEAT Ack * ------------------------------------------------------------------------- */ static int mtpu_asps_hbeat_ack(queue_t * q, mblk_t * pdu) { (void) q; (void) pdu; ptrace(("Received HBEAT ACK at MTPU RD(q)\n")); /* this is an error */ never(); return (-EFAULT); } /* * ASPT ASPAC Ack * ------------------------------------------------------------------------- * When we are binding, we will be given the first ASPT ASPAC Ack that takes * the AS to the AS-ACTIVE state. If we are in the NS_WACK_BREQ state, then * this message indicates that we can reply with an N_BIND_ACK. */ static int mtpu_aspt_aspac_ack(queue_t * q, mblk_t * pdu) { dp_t *dp = (dp_t *) q->q_ptr; (void) dp; /* FIXME: this should really be MTP RESTART ENDS */ (void) pdu; if (dp->state == NS_WACK_BREQ) return mtpu_bind_ack(q, dp, dp->as); rare(); return (-EFAULT); } /* * ASPT ASPIA Ack * ------------------------------------------------------------------------- * When we are unbinding, we will be given the last ASPT ASPIA Ack the takes * the AS to the AS-INACTIVE state. If we are in the NS_WACK_UREQ state, then * this message indicates that we can reply with an N_OK_ACK. */ static int mtpu_aspt_aspia_ack(queue_t * q, mblk_t * pdu) { dp_t *dp = (dp_t *) q->q_ptr; (void) pdu; if (dp->state == NS_WACK_UREQ) return mtpu_ok_ack(q, N_UNBIND_REQ); rare(); return (-EFAULT); } /* * XFER DATA: * ------------------------------------------------------------------------- * When we receive transfer data, we pass this data up to the MTP-User if the * interface is is the correct state. If the interface is not in the correct * state, what we need to do is to reroute this data back to the next choice in * the Application Server. If the interface is in the ASP-PENDING state for * the AS, we will buffer the data; otherwise we dicard the data. */ static int mtpu_xfer_data(queue_t * q, mblk_t * pdu) { dp_t *dp = (dp_t *) q->q_ptr; xp_t *xp = dp->xp; gp_t *gp = xp->gp; mblk_t *mp; parm_t data; N_unitdata_ind_t *p; mtp_addr_t src = { 0, }, dst = { 0,}; N_qos_sel_data_mtp_t *qos; size_t mlen = sizeof(*p) + sizeof(*src) + sizeof(*dst) + sizeof(*qos); if (dp->state != NS_IDLE) goto pending; fixme(("Make sure that we haven't already written this correlation id first\n")); if (!ua_find_parm(pdu, M3UA_PARM_PROT_DATA, &data)) goto eproto; if (!(mp = ua_allocb(mlen, BPRI_MED))) goto enobufs; mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_UNITDATA_IND; p->SRC_length = sizeof(*src); p->SRC_offset = sizeof(*p); p->DEST_length = sizeof(*dst); p->DEST_offset = sizeof(*p) + sizeof(*src); p->ERROR_type = 0; /* reserved */ src = ((typeof(src)) mp->b_wptr)++; src->ni = data.u.cptr[9]; src->pc = ntohl(data.u.wptr[0]); dst = ((typeof(dst)) mp->b_wptr)++; dst->ni = data.u.cptr[9]; dst->pc = ntohl(data.u.wptr[1]); qos = ((typeof(qos)) mp->b_wptr)++; qos->sls = data.u.cptr[11]; qos->mp = data.u.cptr[10]; pdu->b_rptr = (unsigned char *) data.u.cptr; pdu->b_wptr = pdu->b_rptr + data.len; mp->b_cont = pdu; putq(q, mp); return (QR_ABSORBED); pending: if (gp->state != ASP_PENDING) goto efault; bufq_queue(&gp->buf, pdu); return (QR_ABSORBED); efault: return (-EFAULT); enobufs: return (-ENOBUFS); eproto: return (-EPROTO); } /* * SSNM Messages * ------------------------------------------------------------------------- */ static int mtpu_ssnm(queue_t * q, mblk_t * pdu, ulong etype, ulong prio) { mblk_t *mp; N_uderror_ind_t *p; mtp_addr_t *dst; N_qos_sel_data_mtp_t *qos; size_t mlen = sizeof(*p) + sizeof(*dst) + sizeof(*qos); parm_t apc; if (!ua_find_parm(pdu, UA_PARM_APC, &apc)) { fixme(("Handle clustered version\n")); if ((mp = ua_allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_UDERROR_IND; p->DEST_length = sizeof(*dst); p->DEST_offset = sizeof(*p); p->RESERVED_field = 0; p->ERROR_type = etype | (ntohl(apc.u.wptr[0]) & 0xff000000 ? MTP_CLUSTER : 0); dst = ((typeof(dst)) mp->b_wptr)++; dst->ni = 0; dst->pc = ntohl(apc.u.wptr[0]) & 0x00ffffff; qos = ((typeof(qos)) mp->b_wptr)++; qos->sls = -1UL; qos->mp = prio; putq(q, mp); return (0); } return (-ENOBUFS); } never(); return (-EPROTO); } /* * SSNM DUNA: * ------------------------------------------------------------------------- * When we receive a DUNA we just send an N_UDERROR_IND to the MTP-User with * the appropriate error type. */ static int mtpu_ssnm_duna(queue_t * q, mblk_t * pdu) { return mtpu_ssnm(q, pdu, MTP_DEST_PROHIBITED, 0); } /* * SSNM DAVA: * ------------------------------------------------------------------------- * When we receive a DAVA, we just send an N_UDERROR_IND to the MTP-User with * the appropriate error type. */ static int mtpu_ssnm_dava(queue_t * q, mblk_t * pdu) { return mtpu_ssnm(q, pdu, MTP_DEST_AVAILABLE, 0); } /* * SSNM DAUD: * ------------------------------------------------------------------------- * We should never receive a DAUD. */ static int mtpu_ssnm_daud(queue_t * q, mblk_t * pdu) { (void) q; (void) pdu; ptrace(("Received SSNM DAUD at MTPU RD(q)\n")); /* this is an error */ never(); return (-EFAULT); } /* * SSNM SCON: * ------------------------------------------------------------------------- * When we receive a SCON, we just send an N_UDERROR_IND to the MTP-User with * the appropriate error type and the priority level of the congestion. */ static int mtpu_ssnm_scon(queue_t * q, mblk_t * pdu) { uint level; parm_t cong; if (!ua_find_parm(pdu, M3UA_PARM_CONG_IND, &cong)) { switch ((level = cong.val)) { case 1: case 2: case 3: return mtpu_ssnm(q, pdu, MTP_DEST_CONGESTED, level); default: case 0: /* huh ??? */ rare(); return (-EPROTO); } } rare(); return (-EPROTO); } /* * SSNM DUPU: * ------------------------------------------------------------------------- * When we receive a DUPU we just send an N_UDERROR_IND to the MTP-User with * the appropriate error type. */ static int mtpu_ssnm_dupu(queue_t * q, mblk_t * pdu) { parm_t uc; if (!ua_find_parm(pdu, M3UA_PARM_USER_CAUSE, &uc)) { switch (uc.val >> 16) { default: case M3UA_CAUSE_UNKNOWN: return mtpu_ssnm(q, pdu, MTP_USER_PART_UNKNOWN, 0); case M3UA_CAUSE_UNEQUIPPED: return mtpu_ssnm(q, pdu, MTP_USER_PART_UNEQUIPPED, 0); case M3UA_CAUSE_INACCESSIBLE: return mtpu_ssnm(q, pdu, MTP_USER_PART_UNAVAILABLE, 0); } } rare(); return (-EPROTO); } /* * SSNM DRST: * ------------------------------------------------------------------------- * We should never receive these here. DRST must be handled by the underlying * M3UA layer and should never be propagated to the MTP-User. */ static int mtpu_ssnm_drst(queue_t * q, mblk_t * pdu) { (void) q; (void) pdu; ptrace(("Received SSNM DRST at MTPU RD(q)\n")); /* this is an error */ never(); return (-EFAULT); } /* * RKMM REG RSP: * ------------------------------------------------------------------------- */ static int mtpu_rkmm_reg_rsp(queue_t * q, mblk_t * pdu) { (void) q; (void) pdu; fixme(("Complete this function\n")); return (-EFAULT); } /* * RKMM DEREG RSP: * ------------------------------------------------------------------------- */ static int mtpu_rkmm_dereg_rsp(queue_t * q, mblk_t * pdu) { (void) q; (void) pdu; fixme(("Complete this function\n")); return (-EFAULT); } static int mtpu_msg(queue_t * q, mblk_t * mp) { int err; char *clas; uint32_t mhdr = *((uint32_t *) mp->b_rptr); switch (UA_MSG_CLAS(mhdr)) { case UA_CLASS_MGMT: clas = "MGMT"; switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_MGMT_ERR): return mtpu_mgmt_err(q, mp); case __UA_MSG_TYPE(UA_MGMT_NTFY): return mtpu_mgmt_ntfy(q, mp); } ptrace(("Unexpected MGMT message\n")); goto mtpu_msg_unsupported_message_type; case UA_CLASS_XFER: clas = "XFER"; switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(M3UA_XFER_DATA): return mtpu_xfer_data(q, mp); } ptrace(("Unexpected XFER message\n")); goto mtpu_msg_unsupported_message_type; case UA_CLASS_SSNM: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_SSNM_DUNA): return mtpu_ssnm_duna(q, mp); case __UA_MSG_TYPE(UA_SSNM_DAVA): return mtpu_ssnm_dava(q, mp); case __UA_MSG_TYPE(UA_SSNM_DAUD): return mtpu_ssnm_daud(q, mp); case __UA_MSG_TYPE(UA_SSNM_SCON): return mtpu_ssnm_scon(q, mp); case __UA_MSG_TYPE(UA_SSNM_DUPU): return mtpu_ssnm_dupu(q, mp); case __UA_MSG_TYPE(UA_SSNM_DRST): return mtpu_ssnm_drst(q, mp); } ptrace(("Unexpected SSNM message\n")); goto mtpu_msg_unsupported_message_type; case UA_CLASS_ASPS: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_ASPS_ASPUP_REQ): case __UA_MSG_TYPE(UA_ASPS_ASPDN_REQ): break; case __UA_MSG_TYPE(UA_ASPS_ASPUP_ACK): return mtpu_asps_aspup_ack(q, mp); case __UA_MSG_TYPE(UA_ASPS_ASPDN_ACK): return mtpu_asps_aspdn_ack(q, mp); case __UA_MSG_TYPE(UA_ASPS_HBEAT_REQ): return mtpu_asps_hbeat_req(q, mp); case __UA_MSG_TYPE(UA_ASPS_HBEAT_ACK): return mtpu_asps_hbeat_ack(q, mp); } ptrace(("Unexpected ASPS message\n")); goto mtpu_msg_unsupported_message_type; case UA_CLASS_ASPT: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_ASPT_ASPAC_REQ): case __UA_MSG_TYPE(UA_ASPT_ASPIA_REQ): break; case __UA_MSG_TYPE(UA_ASPT_ASPAC_ACK): return mtpu_aspt_aspac_ack(q, mp); case __UA_MSG_TYPE(UA_ASPT_ASPIA_ACK): return mtpu_aspt_aspia_ack(q, mp); } ptrace(("Unexpected ASPT message\n")); goto mtpu_msg_unsupported_message_type; case UA_CLASS_Q921: ptrace(("Unexpected Q921 message\n")); goto mtpu_msg_unsupported_message_class; case UA_CLASS_MAUP: ptrace(("Unexpected MAUP message\n")); goto mtpu_msg_unsupported_message_class; case UA_CLASS_CNLS: ptrace(("Unexpected CNLS message\n")); goto mtpu_msg_unsupported_message_class; case UA_CLASS_CONS: ptrace(("Unexpected CONS message\n")); goto mtpu_msg_unsupported_message_class; case UA_CLASS_RKMM: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_RKMM_REG_REQ): case __UA_MSG_TYPE(UA_RKMM_DEREG_REQ): break; case __UA_MSG_TYPE(UA_RKMM_REG_RSP): return mtpu_rkmm_reg_rsp(q, mp); case __UA_MSG_TYPE(UA_RKMM_DEREG_RSP): return mtpu_rkmm_dereg_rsp(q, mp); } ptrace(("Unexpected RKMM message\n")); goto mtpu_msg_unsupported_message_type; case UA_CLASS_TDHM: ptrace(("Unexpected TDHM message\n")); goto mtpu_msg_unsupported_message_class; case UA_CLASS_TCHM: ptrace(("Unexpected TCHM message\n")); goto mtpu_msg_unsupported_message_class; default: ptrace(("Unexpected message class %d\n", UA_MSG_CLAS(mhdr))); goto mtpu_msg_unsupported_message_class; } mtpu_msg_unsupported_message_type: if ((err = ua_reply_err(q, OTHER(q), UA_ECODE_UNSUPPORTED_MESSAGE_TYPE, NULL, NULL, NULL, mp))) return (err); return (-EPROTO); mtpu_msg_unsupported_message_class: if ((err = ua_reply_err(q, OTHER(q), UA_ECODE_UNSUPPORTED_MESSAGE_CLASS, NULL, NULL, NULL, mp))) return (err); return (-EPROTO); } /* * M_DATA Handling * ------------------------------------------------------------------------- * When an M_CTL or M_DATA is found on the read queue to the MTP-User, we must * convert the M3UA primitive to an MTP-User interface primitive and place the * MTP-User primitive back on the queue. M_CTL M3UA primitives normally * translate to M_PCPROTO primitives. M_DATA M3UA primitives normally * translate to M_PROTO primitives. */ static int mtpu_r_data(queue_t * q, mblk_t * mp) { return mtpu_msg(q, mp); } /* * M_CTL Handling * ------------------------------------------------------------------------- * When an M_CTL or M_DATA is found on the read queue to the MTP-User, we must * convert the M3UA primitive to an MTP-User interface primitive and place the * MTP-User primitive back on the queue. M_CTL M3UA primitives normally * translate to M_PCPROTO primitives. M_DATA M3UA primitives normally * translate to M_PROTO primitives. */ static int mtpu_r_ctl(queue_t * q, mblk_t * mp) { return mtpu_msg(q, mp); } /* * M_ERROR Handling * ------------------------------------------------------------------------- * If we read an M_ERROR from below, we should alter the interface state and * place the AS-U in the down state. Then we can propagate the M_ERROR upwards * to the stream head. No further operations on the stream are possible until * the stream is closed and reopened. */ static int mtpu_r_error(queue_t * q, mblk_t * mp) { fixme(("Place the AS in the down state\n")); return (QR_PASSFLOW); /* pass along */ } /* * M_HANGUP Handling * ------------------------------------------------------------------------- * If we read an M_HANGUP from below, we should alter the interface state and * place the AS-U in the down state. Then we can propagate the M_HANGUP * upwards to the stream head. No further operations on the stream are * possible until the stream is closed and reopened. */ static int mtpu_r_hangup(queue_t * q, mblk_t * mp) { fixme(("Place the AS in the down state\n")); return (QR_PASSFLOW); /* pass along */ } /* * M3UA --> MTP-User (AS) Primitives * ------------------------------------------------------------------------- */ static int mtpu_r_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: return mtpu_r_data(q, mp); case M_CTL: return mtpu_r_ctl(q, mp); case M_ERROR: return mtpu_r_error(q, mp); case M_HANGUP: return mtpu_r_hangup(q, mp); case M_FLUSH: return ua_r_flush(q, mp); } return (QR_PASSFLOW); /* pass along */ } /* * ------------------------------------------------------------------------- * * MTP Provider (MTPP) Stream Message Handling * * ------------------------------------------------------------------------- * * M3UA --> MTP Provider Primitives * ------------------------------------------------------------------------- * This is the MTPP lower write routine. This takes M3UA M_DATA and M_CTL * messages and converts them to MTP Primitives. * * These are primitives sent to a local MTP Provider from the M3UA layer on the * lower mux stream. This is part of the NIF (Nodal Interworking Function) at * an SGP (Signalling Gateway Process). Each message is converted from an M3UA * message to an MTP request or response primitive. These functions are pure * translation functions. When they return zero, they return the M3UA message * translated in `mpp'. When they return negative, they return an error and * `mpp'is untouched. , BPRI_MED*/ static int mtpp_asps_aspup_req(queue_t * q, mblk_t * mp) { (void) q; (void) mp; __ptrace(("SWERR: Received ASPUP REQ at MTPP WR(q)\n")); return (-EFAULT); } static int mtpp_asps_aspdn_req(queue_t * q, mblk_t * mp) { (void) q; (void) mp; __ptrace(("SWERR: Received ASPDN REQ at MTPP WR(q)\n")); return (-EFAULT); } static int mtpp_asps_hbeat_req(queue_t * q, mblk_t * mp) { (void) q; (void) mp; __ptrace(("SWERR: Received HBEAT REQ at MTPP WR(q)\n")); return (-EFAULT); } static int mtpp_asps_hbeat_ack(queue_t * q, mblk_t * mp) { (void) q; (void) mp; __ptrace(("SWERR: Received HBEAT ACK at MTPP WR(q)\n")); return (-EFAULT); } /* * ASPT ASPAC REQ * ------------------------------ */ static int mtpp_aspt_aspac_req(queue_t * q, mblk_t * pdu) { lp_t *lp = (lp_t *) q->q_ptr; mblk_t *mp; mtp_addr_t *a, *rk; N_bind_req_t *p; static const size_t mlen = sizeof(*p) + sizeof(mtp_addr_t); if (lp->state == NS_UNBND) { __ptrace(("SWERR: Attempt to double bind\n")); return (-EFAULT); } if (!(rk = (typeof(rk)) lp->as->rk)) { __ptrace(("SWERR: Missing Routing Key\n")); return (-EFAULT); } if ((mp = ua_allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (((typeof(p)) mp->b_wptr))++; p->PRIM_type = N_BIND_REQ; p->ADDR_length = sizeof(*a); p->ADDR_offset = sizeof(*p); p->CONIND_number = 0; p->BIND_flags = 0; p->PROTOID_length = 0; p->PROTOID_offset = 0; a = (((typeof(a)) mp->b_wptr))++; a->ni = rk->ni; a->si = rk->si; a->pc = rk->pc; lp->state = NS_WACK_BREQ; putnext(q, mp); return (0); } return (-ENOBUFS); } static int mtpp_aspt_aspia_req(queue_t * q, mblk_t * pdu) { lp_t *lp = (lp_t *) q->q_ptr; mblk_t *mp; N_unbind_req_t *p; static const size_t mlen = sizeof(*p); if (lp->state != NS_IDLE) { __ptrace(("SWERR: Attempt to double unbind\n")); return (-EFAULT); } if ((mp = ua_allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (((typeof(p)) mp->b_wptr))++; p->PRIM_type = N_UNBIND_REQ; lp->state = NS_WACK_UREQ; putnext(q, mp); return (0); } return (-ENOBUFS); } static int mtpp_xfer_data(queue_t * q, mblk_t * pdu) { mblk_t *mp; parm_t data; N_unitdata_req_t *p; N_qos_sel_data_mtp_t *qos; mtp_addr_t *dst; size_t mlen = sizeof(*p) + sizeof(*dst) + sizeof(*qos); fixme(("Make sure that we haven't already written this correlation id first\n")); if (!ua_find_parm(pdu, M3UA_PARM_PROT_DATA, &data)) return (-EPROTO); if ((mp = ua_allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_UNITDATA_REQ; p->DEST_length = sizeof(*dst); p->DEST_offset = sizeof(*p); p->RESERVED_field[0] = 0; p->RESERVED_field[1] = 0; dst = ((typeof(dst)) mp->b_wptr)++; dst->ni = data.u.cptr[9]; dst->pc = ntohl(data.u.wptr[1]); qos = ((typeof(qos)) mp->b_wptr)++; qos->sls = data.u.cptr[11]; qos->mp = data.u.cptr[10]; pdu->b_rptr = (unsigned char *) data.u.cptr; pdu->b_wptr = pdu->b_rptr + data.len; mp->b_cont = pdu; putq(q, mp); return (QR_ABSORBED); } return (-ENOBUFS); } static int mtpp_ssnm_scon(queue_t * q, mblk_t * pdu) { (void) q; (void) pdu; fixme(("We don't support writing SCONs yet...\nImplement as N_OPTMGMT_REQ\n")); return (-EFAULT); } static int mtpp_rkmm_reg_req(queue_t * q, mblk_t * mp) { (void) q; (void) mp; ptrace(("SWERR: Received REG REQ at MTPP WR(q)\n")); return (-EFAULT); } static int mtpp_rkmm_dereg_req(queue_t * q, mblk_t * mp) { (void) q; (void) mp; ptrace(("SWERR: Received DEREG REQ at MTPP WR(q)\n")); return (-EFAULT); } static int mtpp_msg(queue_t * q, mblk_t * mp) { uint32_t mhdr = *((uint32_t *) mp->b_rptr); switch (UA_MSG_CLAS(mhdr)) { case UA_CLASS_MGMT: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_MGMT_ERR): /* never return an error to an error */ return (0); case __UA_MSG_TYPE(UA_MGMT_NTFY): break; } ptrace(("Unexpected MGMT message\n")); goto mtpp_msg_unsupported_message_type; case UA_CLASS_XFER: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(M3UA_XFER_DATA): return mtpp_xfer_data(q, mp); } ptrace(("Unexpected XFER message\n")); goto mtpp_msg_unsupported_message_type; case UA_CLASS_SSNM: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_SSNM_DUNA): case __UA_MSG_TYPE(UA_SSNM_DAVA): case __UA_MSG_TYPE(UA_SSNM_DAUD): case __UA_MSG_TYPE(UA_SSNM_DUPU): case __UA_MSG_TYPE(UA_SSNM_DRST): break; case __UA_MSG_TYPE(UA_SSNM_SCON): return mtpp_ssnm_scon(q, mp); } ptrace(("Unexpected SSNM message\n")); goto mtpp_msg_unsupported_message_type; case UA_CLASS_ASPS: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_ASPS_ASPUP_ACK): case __UA_MSG_TYPE(UA_ASPS_ASPDN_ACK): break; case __UA_MSG_TYPE(UA_ASPS_ASPUP_REQ): return mtpp_asps_aspup_req(q, mp); case __UA_MSG_TYPE(UA_ASPS_ASPDN_REQ): return mtpp_asps_aspdn_req(q, mp); case __UA_MSG_TYPE(UA_ASPS_HBEAT_REQ): return mtpp_asps_hbeat_req(q, mp); case __UA_MSG_TYPE(UA_ASPS_HBEAT_ACK): return mtpp_asps_hbeat_ack(q, mp); } ptrace(("Unexpected ASPS message\n")); goto mtpp_msg_unsupported_message_type; case UA_CLASS_ASPT: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_ASPT_ASPAC_ACK): case __UA_MSG_TYPE(UA_ASPT_ASPIA_ACK): break; case __UA_MSG_TYPE(UA_ASPT_ASPAC_REQ): return mtpp_aspt_aspac_req(q, mp); case __UA_MSG_TYPE(UA_ASPT_ASPIA_REQ): return mtpp_aspt_aspia_req(q, mp); } ptrace(("Unexpected ASPT message\n")); goto mtpp_msg_unsupported_message_type; case UA_CLASS_Q921: ptrace(("Unexpected Q921 message\n")); goto mtpp_msg_unsupported_message_class; case UA_CLASS_MAUP: ptrace(("Unexpected MAUP message\n")); goto mtpp_msg_unsupported_message_class; case UA_CLASS_CNLS: ptrace(("Unexpected CNLS message\n")); goto mtpp_msg_unsupported_message_class; case UA_CLASS_CONS: ptrace(("Unexpected CONS message\n")); goto mtpp_msg_unsupported_message_class; case UA_CLASS_RKMM: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_RKMM_REG_RSP): case __UA_MSG_TYPE(UA_RKMM_DEREG_RSP): break; case __UA_MSG_TYPE(UA_RKMM_REG_REQ): return mtpp_rkmm_reg_req(q, mp); case __UA_MSG_TYPE(UA_RKMM_DEREG_REQ): return mtpp_rkmm_dereg_req(q, mp); } ptrace(("Unexpected RKMM message\n")); goto mtpp_msg_unsupported_message_type; case UA_CLASS_TDHM: ptrace(("Unexpected TDHM message\n")); goto mtpp_msg_unsupported_message_class; case UA_CLASS_TCHM: ptrace(("Unexpected TCHM message\n")); goto mtpp_msg_unsupported_message_class; default: ptrace(("Unexpected message class %d\n", UA_MSG_CLAS(mhdr))); goto mtpp_msg_unsupported_message_class; } mtpp_msg_unsupported_message_type: return ua_reply_err(q, OTHER(q), UA_ECODE_UNSUPPORTED_MESSAGE_TYPE, NULL, NULL, NULL, mp); mtpp_msg_unsupported_message_class: return ua_reply_err(q, OTHER(q), UA_ECODE_UNSUPPORTED_MESSAGE_CLASS, NULL, NULL, NULL, mp); } static int mtpp_w_data(queue_t * q, mblk_t * mp) { return mtpp_msg(q, mp); } static int mtpp_w_ctl(queue_t * q, mblk_t * mp) { return mtpp_msg(q, mp); } static int mtpp_w_error(queue_t * q, mblk_t * mp) { (void) q; (void) mp; fixme(("Write this function\n")); return (-EFAULT); } static int mtpp_w_hangup(queue_t * q, mblk_t * mp) { (void) q; (void) mp; fixme(("Write this function\n")); return (-EFAULT); } static int mtpp_w_prim(queue_t * q, mblk_t * mp) { trace(); switch (mp->b_datap->db_type) { case M_DATA: return mtpp_w_data(q, mp); case M_CTL: return mtpp_w_ctl(q, mp); case M_ERROR: return mtpp_w_error(q, mp); case M_HANGUP: return mtpp_w_hangup(q, mp); case M_FLUSH: return ua_w_flush(q, mp); } return (QR_PASSFLOW); } /* * MTP Provider --> M3UA Primitives * ------------------------------------------------------------------------- * This is the MTPP lower read routine. This takes MTP Primitives and converts * them to M3UA M_DATA and M_CTL messages, if required. If the user is a local * AS, then the primitives are forwarded as is. * * These are primitives received by the M3UA layer from the MTPP (MTP Provider) * lower mux stream. This is aprt of the NIF (Nodal Interworking Function) at * an SGP (Signalling Gateway Process). Each message is converted from an MTP * indication or confirmation primitive to an M3UA (SGP->ASP) message. These * functions are pure translation functions. When they return zero, they * return the MTPP primitive translated into an M3UA messge in `mpp'. When * they return negative, they return an error number and `mpp' is untouched. */ /* * MTPP N_INFO_ACK * ----------------------------------- */ static int mtpp_info_ack(queue_t * q, mblk_t * mp) { /* * We can safely ignore these until we implement * draft-bidulock-sigtran-reginfo-00.txt */ __ptrace(("SWERR: Got unexpected N_INFO_ACK\n")); return (-EFAULT); } /* * MTPP N_BIND_ACK * ----------------------------------- */ static int mtpp_bind_ack(queue_t * q, mblk_t * mp) { int err; lp_t *lp = (lp_t *) q->q_ptr; if (lp->state != NS_WACK_BREQ) { __ptrace(("SWERR: Got N_BIND_ACK in incorrect state\n")); return (-EFAULT); } if ((err = sg_active_ack(q, lp->as))) return (err); /* finished processing N_BIND_ACK */ lp->state = NS_IDLE; return (0); } /* * MTPP N_ERROR_ACK * ----------------------------------- */ static int mtpp_error_ack(queue_t * q, mblk_t * mp) { int err; lp_t *lp = (lp_t *) q->q_ptr; N_error_ack_t *p = (typeof(p)) mp->b_rptr; if (p->ERROR_prim != N_BIND_REQ || lp->state != NS_WACK_BREQ) { __ptrace(("SWERR: Got unexpected N_ERROR_ACK\n")); return (-EFAULT); } if ((err = sg_active_nack(q, lp->as))) return (err); lp->state = 0; return (0); } /* * MTPP N_OK_ACK * ----------------------------------- * When we get an OK in N_WACK_UREQ we need to move the AS-P from AS_WACK_ASPIA * state to AS_INACTIVE if we are the last active provider. */ static int mtpp_ok_ack(queue_t * q, mblk_t * mp) { int err; lp_t *lp = (lp_t *) q->q_ptr; N_ok_ack_t *p = (typeof(p)) mp->b_rptr; if (p->CORRECT_prim != N_UNBIND_REQ || lp->state != NS_WACK_UREQ) { __ptrace(("SWERR: Got unexpected N_OK_ACK\n")); return (-EFAULT); } if ((err = sg_inactive_ack(q, lp->as))) return (err); lp->state = NS_UNBND; return (0); } static int mtpp_unitdata_ind(queue_t * q, mblk_t * pdu) { mblk_t *mp; lp_t *lp = (lp_t *) q->q_ptr; as_t *as = lp->as; N_uderror_ind_t *p = (typeof(p)) pdu->b_rptr; mtp_addr_t *dst = (typeof(dst)) (pdu->b_rptr + p->DEST_offset); N_qos_sel_data_mtp_t *qos = (typeof(qos)) (dst + 1); size_t mlen = 10 * sizeof(uint32_t); size_t dlen = msgdsize(pdu->b_cont); if ((mp = ua_allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; *((uint32_t *) mp->b_wptr)++ = M3UA_XFER_DATA; *((uint32_t *) mp->b_wptr)++ = htonl(mlen + UA_PAD4(dlen)); fixme(("Actually pad the data\n")); *((uint32_t *) mp->b_wptr)++ = UA_PARM_RC; *((uint32_t *) mp->b_wptr)++ = htonl(as->rc); *((uint32_t *) mp->b_wptr)++ = UA_PARM_CORR_ID; *((uint32_t *) mp->b_wptr)++ = htonl(as->cid++); *((uint32_t *) mp->b_wptr)++ = UA_PHDR(M3UA_PARM_PROT_DATA, dlen); fixme(("Source address must come from bind\n")); *((uint32_t *) mp->b_wptr)++ = 0; *((uint32_t *) mp->b_wptr)++ = htonl(dst->pc); *(mp->b_wptr)++ = dst->si; *(mp->b_wptr)++ = dst->ni; *(mp->b_wptr)++ = qos->mp; *(mp->b_wptr)++ = qos->sls; mp->b_cont = pdu->b_cont; pdu->b_cont = NULL; fixme(("Put this block somewhere\n")); return (0); } return (-ENOBUFS); } static int mtpp_uderror_ind(queue_t * q, mblk_t * pdu) { mblk_t *mp; lp_t *lp = (lp_t *) q->q_ptr; as_t *as = lp->as; N_unitdata_ind_t *p = (typeof(p)) pdu->b_rptr; mtp_addr_t *dst = (typeof(dst)) (pdu->b_rptr + p->DEST_offset); ulong etype = p->ERROR_type; switch (etype) { case MTP_DEST_AVAILABLE: case MTP_CLUS_AVAILABLE: { size_t mlen = 6 * sizeof(uint32_t); uint32_t apc = dst->pc | (etype & MTP_CLUSTER ? 0x08000000 : 0); if ((mp = ua_allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; *((uint32_t *) mp->b_wptr)++ = UA_SSNM_DAVA; *((uint32_t *) mp->b_wptr)++ = htonl(mlen); *((uint32_t *) mp->b_wptr)++ = UA_PARM_RC; *((uint32_t *) mp->b_wptr)++ = htonl(as->rc); *((uint32_t *) mp->b_wptr)++ = UA_PARM_APC; *((uint32_t *) mp->b_wptr)++ = htonl(apc); fixme(("Put this block somewhere\n")); return (0); } return (-ENOBUFS); } case MTP_RESTARTING: case MTP_DEST_PROHIBITED: case MTP_CLUS_PROHIBITED: { size_t mlen = 6 * sizeof(uint32_t); uint32_t apc = 0; switch (etype) { case MTP_RESTARTING: apc = 0xff000000; break; case MTP_DEST_PROHIBITED: apc = dst->pc; break; case MTP_CLUS_PROHIBITED: apc = dst->pc | 0x08000000; break; } if ((mp = ua_allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; *((uint32_t *) mp->b_wptr)++ = UA_SSNM_DUNA; *((uint32_t *) mp->b_wptr)++ = htonl(mlen); *((uint32_t *) mp->b_wptr)++ = UA_PARM_RC; *((uint32_t *) mp->b_wptr)++ = htonl(as->rc); *((uint32_t *) mp->b_wptr)++ = UA_PARM_APC; *((uint32_t *) mp->b_wptr)++ = htonl(apc); fixme(("Put this block somewhere\n")); return (0); } return (-ENOBUFS); } case MTP_DEST_CONGESTED: case MTP_CLUS_CONGESTED: { size_t mlen = 6 * sizeof(uint32_t); uint32_t apc = dst->pc | (etype & MTP_CLUSTER ? 0x08000000 : 0); if ((mp = ua_allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; *((uint32_t *) mp->b_wptr)++ = UA_SSNM_SCON; *((uint32_t *) mp->b_wptr)++ = htonl(mlen); *((uint32_t *) mp->b_wptr)++ = UA_PARM_RC; *((uint32_t *) mp->b_wptr)++ = htonl(as->rc); *((uint32_t *) mp->b_wptr)++ = UA_PARM_APC; *((uint32_t *) mp->b_wptr)++ = htonl(apc); fixme(("Put this block somewhere\n")); return (0); } return (-ENOBUFS); } case MTP_USER_PART_UNKNOWN: case MTP_USER_PART_UNEQUIPPED: case MTP_USER_PART_UNAVAILABLE: { size_t mlen = 8 * sizeof(uint32_t); uint32_t apc = dst->pc; uint cause = 0; switch (etype) { case MTP_USER_PART_UNKNOWN: cause = M3UA_CAUSE_UNKNOWN; break; case MTP_USER_PART_UNEQUIPPED: cause = M3UA_CAUSE_UNEQUIPPED; break; case MTP_USER_PART_UNAVAILABLE: cause = M3UA_CAUSE_INACCESSIBLE; break; } if ((mp = ua_allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; *((uint32_t *) mp->b_wptr)++ = UA_SSNM_DUPU; *((uint32_t *) mp->b_wptr)++ = htonl(mlen); *((uint32_t *) mp->b_wptr)++ = UA_PARM_RC; *((uint32_t *) mp->b_wptr)++ = htonl(as->rc); *((uint32_t *) mp->b_wptr)++ = UA_PARM_APC; *((uint32_t *) mp->b_wptr)++ = htonl(apc); *((uint32_t *) mp->b_wptr)++ = M3UA_PARM_USER_CAUSE; *((uint16_t *) mp->b_wptr)++ = htons(cause); *((uint16_t *) mp->b_wptr)++ = htons(dst->si); fixme(("Put this block somewhere\n")); return (0); } return (-ENOBUFS); } } ptrace(("Received an N_UDERROR_IND with invalid error type %ld\n", etype)); return (-EFAULT); } static int mtpp_r_proto(queue_t * q, mblk_t * mp) { switch (*((long *) mp->b_rptr)) { case N_INFO_ACK: return mtpp_info_ack(q, mp); case N_BIND_ACK: return mtpp_bind_ack(q, mp); case N_ERROR_ACK: return mtpp_error_ack(q, mp); case N_OK_ACK: return mtpp_ok_ack(q, mp); case N_UNITDATA_IND: return mtpp_unitdata_ind(q, mp); case N_UDERROR_IND: return mtpp_uderror_ind(q, mp); } return (-EOPNOTSUPP); } static int mtpp_r_pcproto(queue_t * q, mblk_t * mp) { return mtpp_r_proto(q, mp); } static int mtpp_r_error(queue_t * q, mblk_t * mp) { int err; lp_t *lp = (lp_t *) q->q_ptr; if (!lp->as) { ptrace(("Received M_ERROR on unallocated XP\n")); return (-EFAULT); } if ((err = sg_inactive_ack(q, lp->as))) return (err); fixme(("Notify LM.\n")); return (0); } static int mtpp_r_hangup(queue_t * q, mblk_t * mp) { int err; lp_t *lp = (lp_t *) q->q_ptr; if (!lp->as) { ptrace(("Received M_HANGUP on unallocated XP\n")); return (-EFAULT); } if ((err = sg_inactive_ack(q, lp->as))) return (err); fixme(("Notify LM.\n")); return (0); } static int mtpp_r_prim(queue_t * q, mblk_t * mp) { trace(); switch (mp->b_datap->db_type) { case M_PROTO: return mtpp_r_proto(q, mp); case M_PCPROTO: return mtpp_r_pcproto(q, mp); case M_ERROR: return mtpp_r_error(q, mp); case M_HANGUP: return mtpp_r_hangup(q, mp); case M_FLUSH: return ua_r_flush(q, mp); } return (-EOPNOTSUPP); } /* * ------------------------------------------------------------------------- * * SCTP NPI Transport Message Handling * * ------------------------------------------------------------------------- */ static queue_t *m3ua_sgp_route(as_t * as, m3ua_parms_t * p, int *err) { (void) as; (void) p; fixme(("Write this function\n")); *err = -EFAULT; return (NULL); } /* * ------------------------------------------------------------------------- * * M3UA --> SCTP NPI Transport Primitives * * ------------------------------------------------------------------------- */ static int spp_w_data(queue_t * q, mblk_t * pdu) { mblk_t *mp; N_data_req_t *p; N_qos_sel_data_sctp_t *qos; const size_t mlen = sizeof(*p) + sizeof(*qos); if ((mp = ua_allocb(q, mlen, BPRI_MED))) { uint32_t mhdr = ((uint32_t *) pdu->b_rptr)[0]; mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_DATA_REQ; p->DATA_xfer_flags = 0; qos = ((typeof(qos)) mp->b_wptr)++; qos->n_qos_type = N_QOS_SEL_DATA_SCTP; qos->ppi = M3UA_PPI; qos->tsn = 0; qos->ssn = 0; mp->b_cont = pdu; switch (UA_MSG_CLAS(mhdr)) { case UA_CLASS_MGMT: case UA_CLASS_ASPS: case UA_CLASS_RKMM: qos->sid = 0; break; case UA_CLASS_ASPT: case UA_CLASS_XFER: case UA_CLASS_SSNM: { uint32_t rc = ((uint32_t *) pdu->b_rptr)[3]; fixme(("Map RC onto a stream id\n")); qos->sid = rc; break; } default: case UA_CLASS_MAUP: case UA_CLASS_Q921: case UA_CLASS_CNLS: case UA_CLASS_CONS: case UA_CLASS_TDHM: case UA_CLASS_TCHM: freeb(mp); return (-EPROTO); } putq(q, mp); return (QR_ABSORBED); } return (-ENOBUFS); } static int spp_w_ctl(queue_t * q, mblk_t * mp) { return spp_w_data(q, mp); } static int spp_w_error(queue_t * q, mblk_t * mp) { fixme(("Disconnect, deactivate and take down ASP and notify LM\n")); return (-EFAULT); } static int spp_w_hangup(queue_t * q, mblk_t * mp) { fixme(("Disconnect, deactivate and take down ASP and notify LM\n")); return (-EFAULT); } static int spp_w_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: return spp_w_data(q, mp); case M_CTL: return spp_w_ctl(q, mp); case M_ERROR: return spp_w_error(q, mp); case M_HANGUP: return spp_w_hangup(q, mp); case M_FLUSH: return ua_w_flush(q, mp); } return (-EOPNOTSUPP); } static int spp_r_prim(queue_t * q, mblk_t * mp) { (void) q; (void) mp; fixme(("write this function\n")); return (-EFAULT); } /* * SCTP NPI Transport --> M3UA (SGP) Primitives * ------------------------------------------------------------------------- */ static int sgp_mgmt_err(queue_t * q, mblk_t * mp) { int err = 0; m3ua_parms_t m = { 0, }; trace(); if ((err = m3ua_decode_parms(mp, &m))) return (-EPROTO); if (!m.common.ecode.u.wptr) return (-EPROTO); switch (m.common.ecode.val) { } return (0); } static int sgp_xfer_data(queue_t * q, mblk_t * mp) { /* * We deliver the data by Routing Context. First we find the AS-U and * then We perform a routing fuction to find the SGP (UA or SS7) and * deliver the message there. Any conversions required by the SGP * (proxying RCs) or SS7-P (converting to MTP-TRANSFER-REQ) are done by * the receiver. */ int err = 0; queue_t *oq; as_t *as; gp_t *gp; m3ua_parms_t m = { 0, }; xp_t *xp = ((lp_t *) q->q_ptr)->xp; trace(); if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: decoding error\n")); goto ua_reply_error; } if (m.prot_data.u.wptr) { err = UA_ECODE_MISSING_PARAMETER; ptrace(("ERROR: missing mand protocol data\n")); goto ua_reply_error; } if (m.prot_data.len > 12) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: protocol data too short\n")); goto ua_reply_error; } if (!m.common.rc.u.wptr && (xp->asat_count == 1)) { /* missing routing context: use default */ static uint32_t rc; rc = htonl(xp->gp->rc); m.common.rc.u.wptr = &rc; m.common.rc.len = 4; m.common.rc.val = xp->gp->rc; } if (!m.common.rc.u.wptr) { err = UA_ECODE_NO_CONFIGURED_AS_FOR_ASP; ptrace(("ERROR: need routing context\n")); goto ua_reply_error; } if (m.common.rc.len != 4) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad routing context\n")); goto ua_reply_error; } for (gp = xp->gp; gp; gp = gp->as.next) if (gp->rc == m.common.rc.val) break; if (!gp || !(as = gp->as.as)) { ptrace(("ERROR: couldn't find AS\n")); goto ua_reply_error; } switch (as->state) { case AS_ACTIVE: break; case AS_UNREG: case AS_INACTIVE: case AS_PENDING: /* give ASP a kick */ if ((err = ua_send_aspia_ack(q, OTHER(q), m.common.rc.val))) return (err); return (0); /* discard */ default: never(); return (0); /* discard */ } if (!(oq = m3ua_sgp_route(as, &m, &err))) { ptrace(("ERROR: couldn't find AS queue\n")); goto ua_reply_error; } putq(oq, mp); return (QR_TRIMMED); ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * SGP SSNM * ----------------------------------- * This is for SSNM received from an ASP at an SGP. There is only two valid * cases of this and those are nodal SCON and DAUD. We simply pass these * messages down to any active SS7-P/SGP lower interfaces and let them deal * with it. SCON and DAUD might not be supported. No routing table updates * need be performed. */ static int sgp_ssnm(queue_t * q, mblk_t * mp) { lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; int i, err; m3ua_parms_t m = { 0, }; trace(); if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: message decoding error\n")); goto ua_reply_error; } if (!m.common.rc.u.wptr && (xp->asat_count == 1)) { /* missing routing context use default */ static uint32_t rc; rc = htonl(xp->gp->rc); m.common.rc.u.wptr = &rc; m.common.rc.len = 4; m.common.rc.val = xp->gp->rc; } if (!m.common.rc.u.wptr) { err = UA_ECODE_NO_CONFIGURED_AS_FOR_ASP; ptrace(("ERROR: routing context required\n")); goto ua_reply_error; } if (m.common.rc.len < 4 || (m.common.rc.len & 0x3)) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad routing context(s)\n")); goto ua_reply_error; } if (!m.common.apc.u.wptr) { err = UA_ECODE_MISSING_PARAMETER; ptrace(("ERROR: missing mandatory APC parameter\n")); goto ua_reply_error; } if (m.common.apc.len < 4 || (m.common.apc.len & 0x3)) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad affected point code(s)\n")); goto ua_reply_error; } if (UA_MSG_TYPE(m.type) == UA_SSNM_SCON && !m.cong_ind.u.wptr) { err = UA_ECODE_MISSING_PARAMETER; ptrace(("ERROR: missing Cong Ind\n")); goto ua_reply_error; } if (UA_MSG_TYPE(m.type) == UA_SSNM_SCON && m.cong_ind.len != 4) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad Cong Ind length\n")); goto ua_reply_error; } for (i = 0; i < (m.common.rc.len >> 2); i++) { gp_t *gp; uint32_t rc = ntohl(m.common.rc.u.wptr[i]); err = 0; for (gp = xp->gp; gp; gp = gp->as.next) if (gp->rc == rc) break; if (!gp) { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: invalid routing context\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_INACTIVE | ASF_WACK_ASPAC)) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: AS is inactive\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_DOWN | ASF_WACK_ASPUP | ASF_WACK_ASPDN)) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: AS is down\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_UNREG | ASF_WRSP_REG | ASF_WRSP_DEREG)) { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: AS not registered\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_BLOCKED)) { err = UA_ECODE_REFUSED_MANAGEMENT_BLOCKING; ptrace(("ERROR: ASP management blocked\n")); goto ua_reply_error_loop; } { as_t *as = gp->as.as; ap_t *ap; for (ap = as->ap; ap; ap = ap->user.next) { as_t *ps = ap->prov.as; gp_t *gp; for (gp = ps->gp; gp; gp = gp->xp.next) { if ((1 << gp->state) & (ASM_ACTV_STATES)) { mblk_t *dp; xp_t *xp = gp->xp.xp; lp_t *lp = xp->pp.pp; if ((dp = dupmsg(mp))) { putq(lp->wq, dp); continue; } rare(); return ua_bufcall(q, 0, BPRI_MED); } } } } ua_reply_error_loop: if (err && (err = ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr + i, m.common.apc.u.wptr, m.na.u.wptr, mp))) return (err); } return (0); ua_reply_error: seldom(); return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * SGP ASPS ASPUP REQ * ----------------------------------- */ static int sgp_asps_aspup_req(queue_t * q, mblk_t * mp) { int err = 0; lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; m3ua_parms_t m = { 0, }; trace(); if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: decoding error\n")); goto ua_reply_error; } if (xp && m.common.aspid.u.wptr && m.common.aspid.val != xp->aspid) { err = UA_ECODE_INVALID_ASP_IDENTIFIER; ptrace(("ERROR: Invalid ASP Identifier\n")); goto ua_reply_error; } if (!xp && !m.common.aspid.u.wptr) { err = UA_ECODE_ASP_IDENTIFIER_REQUIRED; ptrace(("ERROR: ASP Identifier Required\n")); goto ua_reply_error; } if (!xp) { for (xp = lp->sp->xp; xp; xp = xp->sp.next) if (xp->aspid == m.common.aspid.val) break; if (xp->pp.pp) xp = NULL; if (xp) { /* link to the proper xp */ xp->pp.pp = lp; if ((xp->pp.next = lp->xp)) xp->pp.next->pp.prev = &xp->pp.next; xp->pp.prev = &lp->xp; lp->xp = xp; } } if (!xp) { err = UA_ECODE_INVALID_ASP_IDENTIFIER; ptrace(("ERROR: Invalid ASP Identifier\n")); goto ua_reply_error; } if ((1 << xp->state) & (ASPF_BLOCKED)) { err = UA_ECODE_REFUSED_MANAGEMENT_BLOCKING; ptrace(("ERROR: Management blocking\n")); goto ua_reply_error; } { gp_t *gp; xp->state = ASP_WACK_ASPUP; for (gp = xp->gp; gp; gp = gp->xp.next) { xp_t *xp = gp->xp.xp; as_t *as = gp->as.as; if ((1 << gp->state) & (ASM_DOWN_STATES)) { if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); if (gp->flags & AS_STATIC) { xp->asdn_count--; as->xpdn_count--; xp->asia_count++; as->xpia_count++; gp->state = AS_INACTIVE; if (!as->xpac_count) as->state = AS_INACTIVE; continue; } xp->asdn_count--; as->xpdn_count--; xp->asur_count++; as->xpur_count++; gp->state = AS_UNREG; if (!(as->xpac_count + as->xpia_count)) as->state = AS_UNREG; continue; } if ((1 << gp->state) & (ASM_UREG_STATES)) { if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); if (gp->flags & AS_DYNAMIC) { gp->state = AS_UNREG; continue; } xp->asur_count--; as->xpur_count--; xp->asia_count++; as->xpia_count++; gp->state = AS_INACTIVE; if (!as->xpac_count) as->state = AS_INACTIVE; continue; } if ((1 << gp->state) & (ASM_INAC_STATES)) { if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); if (gp->flags & AS_STATIC) { gp->state = AS_INACTIVE; if (!as->xpac_count) as->state = AS_INACTIVE; continue; } gp->state = AS_UNREG; xp->asia_count--; as->xpia_count--; xp->asur_count++; as->xpur_count++; if (!(as->xpac_count + as->xpia_count)) as->state = AS_UNREG; continue; } if ((1 << gp->state) & (ASM_ACTV_STATES)) { if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); if (gp->t_hbt) untimeout(xchg(&gp->t_hbt, 0)); if (as->xpac_count == 1) if ((err = as_inactive_req(q, as))) { if (err < 0) return (err); else break; } if (gp->flags & AS_STATIC) { xp->asac_count--; as->xpac_count--; xp->asia_count++; as->xpia_count++; gp->state = AS_INACTIVE; } else { xp->asac_count--; as->xpac_count--; xp->asur_count++; as->xpur_count++; gp->state = AS_UNREG; } continue; } if ((1 << gp->state) & (ASF_BLOCKED | ASF_PENDING)) { continue; } never(); } if (xp->t_ack) untimeout(xchg(&xp->t_ack, 0)); if (xp->t_hbt) untimeout(xchg(&xp->t_hbt, 0)); if ((err = ua_send_aspup_ack(xp->pp.pp->wq, xp->aspid))) return (err); xp->state = ASP_UP; /* notify the ASP of all the AS in an UP state */ for (gp = xp->gp; gp; gp = gp->xp.next) { as_t *as = gp->as.as; if ((1 << as->state) & (ASM_ACTV_STATES)) { if ((err = ua_send_ntfy(q, OTHER(q), &gp->rc, NULL, UA_STATUS_AS_ACTIVE))) return (err); } else if ((1 << as->state) & (ASM_TPND_STATES)) { if ((err = ua_send_ntfy(q, OTHER(q), &gp->rc, NULL, UA_STATUS_AS_PENDING))) return (err); } else if ((1 << as->state) & (ASM_INAC_STATES)) { if ((err = ua_send_ntfy(q, OTHER(q), &gp->rc, NULL, UA_STATUS_AS_INACTIVE))) return (err); } } return (0); } ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * SGP ASPS ASPDN REQ * ----------------------------------- */ static int sgp_asps_aspdn_req(queue_t * q, mblk_t * mp) { int err = 0; lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; m3ua_parms_t m = { 0, }; trace(); if (!(err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: decoding error\n")); goto ua_reply_error; } if (xp) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: no XP attached\n")); goto ua_reply_error; } { gp_t *gp; xp->state = ASP_WACK_ASPDN; for (gp = xp->gp; gp; gp = gp->xp.next) { xp_t *xp = gp->xp.xp; as_t *as = gp->as.as; switch (gp->state) { case AS_WACK_ASPDN: case AS_WACK_ASPUP: if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); case AS_DOWN: gp->state = AS_DOWN; continue; case AS_WRSP_REG: case AS_WRSP_DEREG: if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); case AS_UNREG: xp->asur_count--; as->xpur_count--; xp->asdn_count++; as->xpdn_count++; gp->state = AS_DOWN; continue; case AS_WACK_ASPAC: if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); case AS_INACTIVE: xp->asia_count--; as->xpia_count--; xp->asdn_count++; as->xpdn_count++; gp->state = AS_DOWN; continue; case AS_WACK_HBEAT: if (gp->t_hbt) untimeout(xchg(&gp->t_hbt, 0)); case AS_WACK_ASPIA: if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); case AS_ACTIVE: /* this is the last active ASP */ if (as->xpac_count == 1) { if ((err = as_inactive_req(q, as))) { if (err < 0) return (err); else break; } } xp->asac_count--; as->xpac_count--; xp->asdn_count++; as->xpdn_count++; gp->state = AS_DOWN; continue; case AS_BLOCKED: continue; } never(); ptrace(("SWERR: GP in unexpected state\n")); } if (xp->t_ack) untimeout(xchg(&xp->t_ack, 0)); if (xp->t_hbt) untimeout(xchg(&xp->t_hbt, 0)); if ((err = ua_send_aspdn_ack(xp->pp.pp->wq))) return (err); xp->state = ASP_DOWN; return (0); } ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * SGP ASPS BEAT REQ * ----------------------------------- */ static int sgp_asps_hbeat_req(queue_t * q, mblk_t * mp) { int err = 0; m3ua_parms_t m = { 0, }; xp_t *xp; /* * BEAT REQ: FIXME: We might want to treate expedited and normal BEAT * REQ differently. If the BEAT REQ is ordered, we might want to push * queues before responding in preparation for extension heartbeat * procedures. For the meantime, we will simply reply with a BEAT ACK. */ trace(); ensure(q, return (-EFAULT)); ensure(q->q_ptr, return (-EFAULT)); xp = ((ua_t *) q->q_ptr)->xp; if (!xp) { return (-EAGAIN); /* not ready to accept messages */ } if (xp->state == ASP_DOWN) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: BEAT unexpected in ASP_DOWN\n")); goto ua_reply_error; } if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: decoding error\n")); goto ua_reply_error; } return ua_send_hbeat_ack(OTHER(q), m.common.hbdata.u.cptr, m.common.hbdata.len); ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * SGP ASPS BEAT ACK * ----------------------------------- */ static int sgp_asps_hbeat_ack(queue_t * q, mblk_t * mp) { int err = 0; m3ua_parms_t m = { 0, }; xp_t *xp = ((ua_t *) q->q_ptr)->xp; trace(); if (!xp) { return (-EAGAIN); /* not ready to accept messages */ } if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: Deconding error\n")); goto ua_reply_error; } if (xp->state == ASP_WACK_HBEAT && m.common.aspid.u.wptr) { /* * We were heartbeating at the ASP level. We really need to * check the heartbeat data to see whether this was in response * to one of our hearbeats at the ASP level or possibly a * heartbeat at the AS level. */ if (xp->t_hbt) untimeout(xchg(&xp->t_hbt, 0)); xp->state = ASP_ACTIVE; return (0); } if (m.common.rc.u.wptr) { /* * We need to check if this was a heartbeat that we sent from * the AS level. For now we will just discard the ack. */ return (0); } return (0); /* just discard for now */ ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * SGP ASPT ASPAC REQ * ----------------------------------- * ASPAC REQ: When an SGP receives an ASPAC REQ, if the associated AS is * already active, we merely relpy with an ASPAC ACK. If the associated AS is * not yet active, we take steps to activate the AS and move it to * AS_WACK_ASPAC state so that when things complete successfully or * unsuccessfully, we can respond. */ static int sgp_aspt_aspac_req(queue_t * q, mblk_t * mp) { int i, err = 0; m3ua_parms_t m = { 0, }; xp_t *xp = ((ua_t *) q->q_ptr)->xp; trace(); if ((1 << xp->state) & (ASPF_DOWN | ASPF_WACK_ASPUP | ASPF_WACK_ASPDN)) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: ASP is down\n")); goto ua_reply_error; } if (xp->state == ASP_BLOCKED) { err = UA_ECODE_REFUSED_MANAGEMENT_BLOCKING; ptrace(("ERROR: ASP is blocked\n")); goto ua_reply_error; } if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: message decode error\n")); goto ua_reply_error; } if (!m.common.rc.u.wptr && (xp->asat_count == 1)) { /* missing routing context: use default */ uint32_t rc; rc = htonl(xp->gp->rc); m.common.rc.u.wptr = &rc; m.common.rc.len = 4; m.common.rc.val = xp->gp->rc; } if (!m.common.rc.u.wptr) { err = UA_ECODE_NO_CONFIGURED_AS_FOR_ASP; ptrace(("ERROR: missing routing context\n")); goto ua_reply_error; } if (m.common.rc.len < 4 || (m.common.rc.len & 0x3)) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad routing context\n")); goto ua_reply_error; } for (i = 0; i < (m.common.rc.len >> 2); i++) { as_t *as; gp_t *gp; uint32_t rc = htonl(*(m.common.rc.u.wptr + i)); err = 0; for (gp = xp->gp; gp; gp = gp->as.next) if (gp->rc == rc) break; if (!gp) { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: invalid routing context\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_WACK_ASPAC)) { err = 0; ptrace(("ERROR: ignoring ASPAC\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASM_DOWN_STATES)) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: AS is DOWN\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASM_UREG_STATES)) { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: registration error\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_BLOCKED)) { err = UA_ECODE_REFUSED_MANAGEMENT_BLOCKING; ptrace(("ERROR: management blocked\n")); goto ua_reply_error_loop; } as = gp->as.as; if (as->tmode && m.common.tmode.u.wptr && as->tmode != m.common.tmode.val) { err = UA_ECODE_UNSUPPORTED_TRAFFIC_MODE; ptrace(("ERROR: bad traffic mode\n")); goto ua_reply_error_loop; } if (as->xpac_count >= as->maxa_count) { err = UA_ECODE_REFUSED_MANAGEMENT_BLOCKING; ptrace(("ERROR: too many ASPs active for AS\n")); goto ua_reply_error_loop; } switch (gp->state) { /* * In this case, this is the last ASP in the AS and it previously send an ASPIA * Req which hasn't been fulfilled yet. We can cancel this request simply by * changing our state and generating an activation request. */ case AS_WACK_ASPIA: if ((err = as_active_req(q, as))) return (err); gp->state = AS_WACK_ASPAC; continue; /* * In this case, the ASP is already in the active state and we can just * acknowledge the ASPAC Req immediately. */ case AS_ACTIVE: if ((err = ua_send_aspac_ack(q, OTHER(q), as->tmode, gp->rc))) return (err); continue; /* * In this case, the ASP is inactive. What we do here depends on the traffic * mode of the Application Server. Override is a little bit different than * Loadshare or Broadcast. */ case AS_INACTIVE: switch (as->tmode) { /* * Override is somewhat different than the other traffic modes when the ASP is * inactive. */ case AS_TMODE_OVERRIDE: switch (as->state) { /* * We must override the other ASP which is in the process of activating. Find * the ASP in this process and steal their AS_WACK_ASPAC. */ case AS_WACK_ASPAC: if ((err = asp_override(q, as)) < 0) return (err); if (err > 0) break; gp->t_ack = timeout(ua_gp_ack_timeout, (caddr_t) gp, ua_t_ack_val); gp->state = AS_WACK_ASPAC; continue; /* * Just do a normal activation: we are the only ASP activating. */ case AS_INACTIVE: if ((err = as_active_req(q, as))) return (err); gp->state = AS_WACK_ASPAC; continue; /* * If the AS is inactivating, we can find the ASPs that requested the * deactivation and fulfill their request and then start an activation of our * own. */ case AS_WACK_ASPIA: if ((err = asp_inactive_ack(q, as))) return (err); if ((err = as_active_req(q, as))) return (err); gp->state = AS_WACK_ASPAC; continue; /* * If the AS is already active, we must inform the active ASP that we are * taking over and place that ASP in the AS_INACTIVE state. */ case AS_ACTIVE: if ((err = asp_override(q, as)) < 0) return (err); if (err > 0) break; if ((err = ua_send_aspac_ack(q, OTHER(q), as->tmode, gp->rc))) return (err); gp->state = AS_ACTIVE; continue; default: never(); err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("SWERR: AS is in state %d\n", as->state)); break; } break; case AS_TMODE_LOADSHARE: case AS_TMODE_BROADCAST: switch (as->state) { /* * Another ASP is in the process of activating the AS. We can just join the * group. */ case AS_WACK_ASPAC: as->xpar_count++; gp->t_ack = timeout(ua_gp_ack_timeout, (caddr_t) gp, ua_t_ack_val); gp->state = AS_WACK_ASPAC; continue; /* * The AS is completely inactive, so we will have to go thru and generate a new * AS activation request. */ case AS_INACTIVE: if ((err = as_active_req(q, as))) return (err); gp->state = AS_WACK_ASPAC; continue; /* * We must fulfill the deactivation request of the ASP waiting on deactivation * and then start our own activation request to cancel the deactivation. */ case AS_WACK_ASPIA: if ((err = asp_inactive_ack(q, as))) return (err); if ((err = as_active_req(q, as))) return (err); gp->state = AS_WACK_ASPAC; continue; /* * The AS is already fully active, we can just join the active AS and * acknowledge immediately. */ case AS_ACTIVE: /* we are already active, just ack */ if ((err = ua_send_aspac_ack(q, OTHER(q), as->tmode, gp->rc))) return (err); /* delayed notification for `n' asps */ if (as->xpac_count == as->mina_count - 1) if ((err = ua_ntfy_as_change(q, as, UA_STATUS_AS_ACTIVE))) return (err); xp->asia_count--; as->xpia_count--; xp->asac_count++; as->xpac_count++; gp->state = AS_ACTIVE; continue; default: never(); err = UA_ECODE_UNEXPECTED_MESSAGE; break; } break; default: never(); err = UA_ECODE_UNEXPECTED_MESSAGE; break; } break; default: never(); err = UA_ECODE_UNEXPECTED_MESSAGE; break; } break; ua_reply_error_loop: if (err && (err = ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr + i, NULL, NULL, mp))) return (err); } return (0); ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * SGP ASPT ASPIA REQ * ----------------------------------- * We have received an ASPIA REQ at an SGP. */ static int sgp_aspt_aspia_req(queue_t * q, mblk_t * mp) { int i, err = 0; m3ua_parms_t m = { 0, }; as_t *as; gp_t *gp; xp_t *xp = ((ua_t *) q->q_ptr)->xp; trace(); if ((1 << xp->state) & (ASPF_DOWN | ASPF_WACK_ASPUP | ASPF_WACK_ASPDN)) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: ASP is down\n")); /* we might want to send ASPIA Ack as well */ goto ua_reply_err; } if ((1 << xp->state) & (ASPF_BLOCKED)) { err = UA_ECODE_REFUSED_MANAGEMENT_BLOCKING; ptrace(("ERROR: ASP is blocked\n")); goto ua_reply_err; } if (!(err = m3ua_decode_parms(mp, &m))) { err = err; ptrace(("ERROR: message decode error\n")); goto ua_reply_err; } if (!m.common.rc.u.wptr && (xp->asat_count == 1)) { /* missing routing context: use default */ uint32_t rc; rc = htonl(xp->gp->rc); m.common.rc.u.wptr = &rc; m.common.rc.len = 4; m.common.rc.val = xp->gp->rc; } if (m.common.rc.u.wptr) { err = UA_ECODE_NO_CONFIGURED_AS_FOR_ASP; ptrace(("ERROR: missing routing context\n")); goto ua_reply_err; } if (m.common.rc.len >= 4 && !(m.common.rc.len & 0x3)) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad routing context\n")); goto ua_reply_err; } for (i = 0; i < (m.common.rc.len >> 2); i++) { uint32_t rc = htonl(*(m.common.rc.u.wptr + i)); err = 0; for (gp = xp->gp; gp; gp = gp->as.next) if (gp->rc == rc) break; if (!gp) { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: invalid routing context\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_WACK_ASPIA)) { err = 0; ptrace(("ERROR: ignoring ASPIA\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASM_DOWN_STATES)) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: AS is DOWN\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASM_UREG_STATES)) { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: registration error\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_BLOCKED)) { err = UA_ECODE_REFUSED_MANAGEMENT_BLOCKING; ptrace(("ERROR: management blocked\n")); goto ua_reply_error_loop; } as = gp->as.as; if (as->tmode && m.common.tmode.u.wptr && as->tmode != m.common.tmode.val) { err = UA_ECODE_UNSUPPORTED_TRAFFIC_MODE; ptrace(("ERROR: bad traffic mode\n")); goto ua_reply_error_loop; } switch (gp->state) { /* * In this case, this is the ASP that still has an unfulfilled ASPAC request in * progress. We want to cancel the ASPAC request and start an ASPIA request. */ case AS_WACK_ASPAC: if ((err = as_inactive_req(q, as))) return (err); gp->state = AS_WACK_ASPIA; continue; /* * In this case, this ASP is already inactive. We can just ack the request. */ case AS_INACTIVE: /* if we are already inactive just ack it */ if ((err = ua_send_aspia_ack(q, OTHER(q), gp->rc))) return (err); continue; /* * In this case, the ASP is active. If deactivating the ASP will not affect * the state of the AS, we can simply acknowledge the deactivation. Otherwise, * we will have to start a deactivation request on the AS. */ case AS_ACTIVE: /* * If we are not the last active ASP in the AS, we can simply satisfy the * request without have to change the state of the AS. */ if (as->xpac_count > 1) { if ((err = ua_send_aspia_ack(q, OTHER(q), gp->rc))) return (err); if (as->xpac_count == as->mina_count) if ((err = ua_ntfy_as_change(q, as, UA_STATUS_AS_INSUFFICIENT_ASPS))) return (err); if (as->xpac_count == as->mina_count + 1) if ((err = ua_ntfy_as_change(q, as, UA_STATUS_AS_MINIMUM_ASPS))) return (err); as->xpac_count--; as->xpia_count++; xp->asac_count--; xp->asia_count++; gp->state = AS_INACTIVE; continue; } /* * If we are the last active ASP in the AS, then we have to start a deactivation request * going. */ if ((err = as_inactive_req(q, as))) return (err); gp->state = AS_WACK_ASPIA; continue; default: never(); err = UA_ECODE_UNEXPECTED_MESSAGE; } ua_reply_error_loop: if (err && (err = ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr + i, NULL, NULL, mp))) return (err); } return (0); ua_reply_err: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * SGP RKMM REG REQ * ----------------------------------- */ static int sgp_rkmm_reg_req(queue_t * q, mblk_t * mp) { trace(); (void) q; (void) mp; fixme(("Wirte this function\n")); return (-EFAULT); } /* * SGP RKMM DEREG REQ * ----------------------------------- */ static int sgp_rkmm_dereg_req(queue_t * q, mblk_t * mp) { trace(); (void) q; (void) mp; fixme(("Wirte this function\n")); return (-EFAULT); } /* * SGP MSG * ----------------------------------- */ static int sgp_msg(queue_t * q, mblk_t * pdu) { mblk_t *mp = pdu->b_cont; uint32_t mhdr = *((uint32_t *) mp->b_rptr); switch (UA_MSG_CLAS(mhdr)) { case UA_CLASS_MGMT: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_MGMT_ERR): return sgp_mgmt_err(q, mp); case __UA_MSG_TYPE(UA_MGMT_NTFY): break; } ptrace(("Unexpected MGMT message\n")); goto sgp_msg_unsupported_message_type; case UA_CLASS_XFER: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(M3UA_XFER_DATA): return sgp_xfer_data(q, mp); } ptrace(("Unexpected XFER message\n")); goto sgp_msg_unsupported_message_type; case UA_CLASS_SSNM: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_SSNM_DUNA): case __UA_MSG_TYPE(UA_SSNM_DAVA): case __UA_MSG_TYPE(UA_SSNM_DUPU): case __UA_MSG_TYPE(UA_SSNM_DRST): break; case __UA_MSG_TYPE(UA_SSNM_DAUD): case __UA_MSG_TYPE(UA_SSNM_SCON): return sgp_ssnm(q, mp); } ptrace(("Unexpected SSNM message\n")); goto sgp_msg_unsupported_message_type; case UA_CLASS_ASPS: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_ASPS_ASPUP_REQ): return sgp_asps_aspup_req(q, mp); case __UA_MSG_TYPE(UA_ASPS_ASPDN_REQ): return sgp_asps_aspdn_req(q, mp); case __UA_MSG_TYPE(UA_ASPS_ASPUP_ACK): break; case __UA_MSG_TYPE(UA_ASPS_ASPDN_ACK): break; case __UA_MSG_TYPE(UA_ASPS_HBEAT_REQ): return sgp_asps_hbeat_req(q, mp); case __UA_MSG_TYPE(UA_ASPS_HBEAT_ACK): return sgp_asps_hbeat_ack(q, mp); } ptrace(("Unexpected ASPS message\n")); goto sgp_msg_unsupported_message_type; case UA_CLASS_ASPT: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_ASPT_ASPAC_REQ): return sgp_aspt_aspac_req(q, mp); case __UA_MSG_TYPE(UA_ASPT_ASPIA_REQ): return sgp_aspt_aspia_req(q, mp); case __UA_MSG_TYPE(UA_ASPT_ASPAC_ACK): break; case __UA_MSG_TYPE(UA_ASPT_ASPIA_ACK): break; } ptrace(("Unexpected ASPT message\n")); goto sgp_msg_unsupported_message_type; case UA_CLASS_Q921: ptrace(("Unexpected Q921 message\n")); goto sgp_msg_unsupported_message_class; case UA_CLASS_MAUP: ptrace(("Unexpected MAUP message\n")); goto sgp_msg_unsupported_message_class; case UA_CLASS_CNLS: ptrace(("Unexpected CNLS message\n")); goto sgp_msg_unsupported_message_class; case UA_CLASS_CONS: ptrace(("Unexpected CONS message\n")); goto sgp_msg_unsupported_message_class; case UA_CLASS_RKMM: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_RKMM_REG_REQ): return sgp_rkmm_reg_req(q, mp); case __UA_MSG_TYPE(UA_RKMM_DEREG_REQ): return sgp_rkmm_dereg_req(q, mp); case __UA_MSG_TYPE(UA_RKMM_REG_RSP): break; case __UA_MSG_TYPE(UA_RKMM_DEREG_RSP): break; } ptrace(("Unexpected RKMM message\n")); goto sgp_msg_unsupported_message_type; case UA_CLASS_TDHM: ptrace(("Unexpected TDHM message\n")); goto sgp_msg_unsupported_message_class; case UA_CLASS_TCHM: ptrace(("Unexpected TCHM message\n")); goto sgp_msg_unsupported_message_class; default: ptrace(("Unexpected message class %d\n", UA_MSG_CLAS(mhdr))); goto sgp_msg_unsupported_message_class; } sgp_msg_unsupported_message_type: ua_reply_err(q, OTHER(q), UA_ECODE_UNSUPPORTED_MESSAGE_TYPE, NULL, NULL, NULL, pdu); return (-EPROTO); sgp_msg_unsupported_message_class: ua_reply_err(q, OTHER(q), UA_ECODE_UNSUPPORTED_MESSAGE_CLASS, NULL, NULL, NULL, pdu); return (-EPROTO); } static int sgp_data_ind(queue_t * q, mblk_t * mp) { return sgp_msg(q, mp); } static int sgp_exdata_ind(queue_t * q, mblk_t * mp) { return sgp_msg(q, mp); } /* * N_RESET_IND * ----------------------------------- * In this situation, the SCTP association has restarted. We need to * reinitialized the relationship from the ASP_DOWN state. */ static int sgp_reset_ind(queue_t * q, mblk_t * mp) { int err; lp_t *lp = (lp_t *) q->q_ptr; sp_t *sp = lp->xp->sp.sp; xp_t *xp; gp_t *gp; for (xp = sp->xp; xp; xp = xp->sp.next) { if (xp != lp->xp) { if ((1 << xp->state) & ~(ASM_DOWN_STATES | ASM_UREG_STATES)) { if ((err = ua_send_ntfy(q, xp->pp.pp->wq, NULL, &lp->xp->aspid, UA_STATUS_ASP_FAILURE))) return (err); } } } /* * We now need to move the XP to the ASP_DOWN state in all the AS to * which it is associated and provide an indication to LM that the * ASP has failed. */ xp = lp->xp; xp->state = ASP_DOWN; /* for each application server */ for (gp = xp->gp; gp; gp = gp->xp.next) { xp_t *xp = gp->xp.xp; as_t *as = gp->as.as; if ((1 << gp->state) & (ASM_DOWN_STATES)) { if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); gp->state = AS_DOWN; continue; } if ((1 << gp->state) & (ASM_UREG_STATES)) { if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); xp->asur_count--; xp->asdn_count++; as->xpur_count--; as->xpdn_count++; gp->state = AS_DOWN; continue; } if ((1 << gp->state) & (ASM_INAC_STATES)) { if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); xp->asia_count--; xp->asdn_count++; as->xpia_count--; as->xpdn_count++; gp->state = AS_DOWN; continue; } if ((1 << gp->state) & (ASM_ACTV_STATES)) { if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); if (gp->t_hbt) untimeout(xchg(&gp->t_hbt, 0)); if (as->xpac_count == 1) { if ((err = as_inactive_req(q, as))) { if (err < 0) return (err); else break; } } xp->asac_count--; xp->asdn_count++; as->xpac_count--; as->xpdn_count++; gp->state = AS_DOWN; continue; } if ((1 << gp->state) & (ASF_BLOCKED | ASF_PENDING)) { continue; } never(); } if (xp->t_ack) untimeout(xchg(&xp->t_ack, 0)); if (xp->t_hbt) untimeout(xchg(&xp->t_hbt, 0)); xp->state = ASP_DOWN; /* * We now need to provide an indication to layer management. We * might want to flush the read and the write queues. */ fixme(("Complete this function\n")); return (0); } /* * N_DISCON_IND * ----------------------------------- * In this situation, the SCTP association with an ASP has aborted. We need * to indicate this situation to the state machines and take the appropriate * action. We must also inform local mangaement so that the SCTP association * can be unlinked and dealt with. */ static int sgp_discon_ind(queue_t * q, mblk_t * mp) { int err; lp_t *lp = (lp_t *) q->q_ptr; sp_t *sp = lp->xp->sp.sp; xp_t *xp; for (xp = sp->xp; xp; xp = xp->sp.next) { if (xp != lp->xp) { if ((1 << xp->state) & ~(ASM_DOWN_STATES | ASM_UREG_STATES)) { if ((err = ua_send_ntfy(q, xp->pp.pp->wq, NULL, &lp->xp->aspid, UA_STATUS_ASP_FAILURE))) return (err); } } } /* * We now need to move the XP to the ASP_DOWN state in all the AS to * which it is associated and provide an indication to LM that the * ASP has failed. */ fixme(("Complete this function\n")); return (-EFAULT); } static int sgp_r_proto(queue_t * q, mblk_t * mp) { switch (*((long *) mp->b_rptr)) { case N_DATA_IND: return sgp_data_ind(q, mp); case N_EXDATA_IND: return sgp_exdata_ind(q, mp); case N_DISCON_IND: return sgp_discon_ind(q, mp); case N_RESET_IND: return sgp_reset_ind(q, mp); default: return (-EOPNOTSUPP); } } static int sgp_r_error(queue_t * q, mblk_t * mp) { (void) q; (void) mp; fixme(("Write this function\n")); return (-EFAULT); } static int sgp_r_hangup(queue_t * q, mblk_t * mp) { (void) q; (void) mp; fixme(("Write this function\n")); return (-EFAULT); } static int sgp_r_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_PROTO: case M_PCPROTO: return sgp_r_proto(q, mp); case M_ERROR: return sgp_r_error(q, mp); case M_HANGUP: return sgp_r_hangup(q, mp); case M_FLUSH: return ua_r_flush(q, mp); } return (-EOPNOTSUPP); } /* * M3UA (SGP) --> SCTP NPI Transport Primitives * ------------------------------------------------------------------------- */ static int sgp_w_data(queue_t * q, mblk_t * pdu) { mblk_t *mp; N_data_req_t *p; N_qos_sel_data_sctp_t *qos; const size_t mlen = sizeof(*p) + sizeof(*qos); if ((mp = ua_allocb(q, mlen, BPRI_MED))) { uint32_t mhdr = *((uint32_t *) pdu->b_rptr); mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_DATA_REQ; p->DATA_xfer_flags = 0; qos = ((typeof(qos)) mp->b_wptr)++; qos->n_qos_type = N_QOS_SEL_DATA_SCTP; qos->ppi = M3UA_PPI; qos->tsn = 0; qos->ssn = 0; mp->b_cont = pdu; pdu->b_datap->db_type = M_DATA; switch (UA_MSG_CLAS(mhdr)) { case UA_CLASS_MGMT: case UA_CLASS_ASPS: case UA_CLASS_RKMM: /* these messages always sent on stream 0 */ qos->sid = 0; break; case UA_CLASS_ASPT: /* FIXME: what about BEAT messages? */ case UA_CLASS_XFER: case UA_CLASS_SSNM: { uint32_t rc = ((uint32_t *) pdu->b_rptr)[3]; fixme(("Map RC onto a stream id\n")); qos->sid = rc; break; } default: case UA_CLASS_MAUP: case UA_CLASS_Q921: case UA_CLASS_CNLS: case UA_CLASS_CONS: case UA_CLASS_TDHM: case UA_CLASS_TCHM: never(); freeb(mp); return (-EFAULT); } putnext(q, mp); return (QR_ABSORBED); } return (-ENOBUFS); } static int sgp_w_ctl(queue_t * q, mblk_t * mp) { return sgp_w_data(q, mp); } static int sgp_w_error(queue_t * q, mblk_t * mp) { fixme(("Disconnect, deactivate and take down SGP and notify LM\n")); return (-EFAULT); } static int sgp_w_hangup(queue_t * q, mblk_t * mp) { fixme(("Disconnect, deactivate and take down SGP and notify LM\n")); return (-EFAULT); } static int sgp_w_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: return sgp_w_data(q, mp); case M_CTL: return sgp_w_ctl(q, mp); case M_ERROR: return sgp_w_error(q, mp); case M_HANGUP: return sgp_w_hangup(q, mp); case M_FLUSH: return ua_w_flush(q, mp); } return (-EOPNOTSUPP); } /* * ========================================================================= * * M3UA ASP (Application Server Process) Message Handling and State Machines * * ========================================================================= */ /* * SCTP NPI Transport --> M3UA (ASP) Primitives * ------------------------------------------------------------------------- * This set of functions handle the receipt of M3UA primitives from an * underlying SCTP transport provider at an ASP (Application Server Process). * Messaging is either handled within the scope of the ASP, or within the scope * of the AS to which the message corresponds. */ /* * ASP ROUTE: * ----------------------------------- * These are the ASP routing function which handle multiple SGs. One function * updates the per-SG routing tables for an Application Server on the basis of * incoming SSNM messages. The other performs the selection of SG and SGP for * outgoing messages. */ static int asp_update_routes(as_t * ps, m3ua_parms_t * m) { int i; as_t *us; struct rt *rt; us = ps->ap->user.as; rt = us->rt; switch (UA_MSG_TYPE(m->type)) { case UA_SSNM_DUNA: /* for each affected point code */ for (i = 0; i < (m->common.apc.len >> 2); i++) { uint32_t apc = ntohl(m->common.apc.u.wptr[i]); (void) apc; /* * Lookup routing entry and mark the given AS-P * unavailable. Delete the routing table entry if all * AS-P are unavailable. */ } case UA_SSNM_DAVA: /* for each affected point code */ for (i = 0; i < (m->common.apc.len >> 2); i++) { uint32_t apc = ntohl(m->common.apc.u.wptr[i]); (void) apc; /* * Lookup the routing entry and mark the given AS-P as * available. If there is no routing table entry, add * one. */ } case UA_SSNM_DRST: /* for each affected point code */ for (i = 0; i < (m->common.apc.len >> 2); i++) { uint32_t apc = ntohl(m->common.apc.u.wptr[i]); (void) apc; /* * Lookup the routing entry and mark the given AS-P as * restricted. If there is no routing table entry, add * one. */ } } return (0); } /* * ASP ROUTE: Here we are routing messages recived from an SGP. This is * performed on the basis of Routing Context alone. */ static queue_t *m3ua_asp_route(as_t * as, m3ua_parms_t * p, int *err) { return (NULL); } /* * MGMT ERR: * ------------------------------------------------------------------------- * IMPLEMENTATION NOTE:- We want to report all errors to management. Some * errors are considered fatal errors. That is, we will stop all processes, * and put everything associated with the ASP into the Down state, even drop * the SCTP association. * * How about if we ignore errors for now and just fall back on our timers? * * * Ok. There are some errors that we cannot ignore. Those are primarily * errors which are in reply to some ASP and AS related activation messages. * For ASP messages we can ignore parameters in the error. For AS related * messages, we need to use the RC from the error unless the error was Invalid * RC. */ /* * ASP ERR XFER DATA * ------------------------------------------------------------------------- * We have received an error message for data we have transfer to the SGP. * Unfortunately, if the diagnostic does not include the entire original DATA * message, we will have lost the DATA. This is a really poor thing with the * M3UA RFC, this data loss can be avoided in several ways: * * 1) negatively acknowledge messages (i.e., place the Correlation Id of * the message into the ERR message so that we can look back in a * temporary hold buffer and recover the message; * * 2) maintain a hold buffer anyways and go scanning the hold buffer * when we receive an error, looking for the first message of a type * which encountered problems and trying to deal with all other * messages of that type; * * 3) always receive the entire message in the diagnostic of the ERR * message so that the message can be recovered and redirected. * * The problem with (1) is that it will probably not be supported by other * implementations. The problem with (3) is that we cannot rely on the other * implementation to always place the entire message in the diagnostic of the * ERR message. (2) can always be done, but is rather complex. */ static int asp_err_xfer_data(queue_t * q, mblk_t * mp, m3ua_parms_t * m, m3ua_parms_t * d) { switch ((m->common.ecode.val)) { case UA_ECODE_INVALID_VERSION: /* * This is a rather confusing error: we were quite * able to establish the association and bring the * ASP UP with the version that we have been using, * but now a DATA message fails? This is probably a * bogus error. Still, we will check the diagnostic * for the recovered message. */ break; case UA_ECODE_UNEXPECTED_MESSAGE: /* * We shouldn't receive these for DATA messages. */ break; case UA_ECODE_PROTOCOL_ERROR: break; case UA_ECODE_INVALID_STREAM_IDENTIFIER: break; case UA_ECODE_INVALID_PARAMETER_VALUE: break; case UA_ECODE_PARAMETER_FIELD_ERROR: /* * We can syntax check the diagnostic (if the message * is there. If we can deocde the message, then we * are going to have trouble with this SGP and we * should take the SGP to the down state (unless, * perhaps if this is the only SGP active). */ break; case UA_ECODE_INVALID_NETWORK_APPEARANCE: /* * We only include NA when an NA has been configred * for an AS. This represents a mismatch between the * SGP and ASP on the value of NA. Resending with no * NA would be fruitless. We should permanently * deactivate this AS for this SGP. */ ptrace(("ERROR: Peer claims NA invalid\n")); break; case UA_ECODE_INVALID_ROUTING_CONTEXT: /* * The AS for which we did send the message is in * trouble. We need to dactivate the AS for this SGP * and start to activate it again. If we do this * several times, we had better lock out the AS for * that SGP. */ break; case UA_ECODE_UNEXPECTED_PARAMETER: case UA_ECODE_MISSING_PARAMETER: /* * We don't miss or add parameters. This is a bogus * error. */ ptrace(("ERROR: Peer claims parameter missing or added when it is not\n")); return (-EPROTO); case UA_ECODE_NO_CONFIGURED_AS_FOR_ASP: /* * We might get this if we did not send a RC in the * DATA message and the other end requires it. We, * however, *always* send RC in data, so this is a * bogus error. */ ptrace(("ERROR: Peer claims RC missing when it is not\n")); return (-EPROTO); default: case UA_ECODE_UNASSIGNED_TEI: /* IUA only */ case UA_ECODE_UNRECOGNIZED_SAPI: /* IUA only */ case UA_ECODE_INVALID_TEI_SAPI_COMBINATION: /* IUA only */ case UA_ECODE_INVALID_IID: /* M2UA only */ case UA_ECODE_UNSUPPORTED_IID_TYPE: /* M2UA only */ case UA_ECODE_ASP_ACTIVE_FOR_IIDS: /* M2UA only */ case UA_ECODE_UNSUPPORTED_MESSAGE_CLASS: case UA_ECODE_UNSUPPORTED_MESSAGE_TYPE: case UA_ECODE_UNSUPPORTED_TRAFFIC_MODE: case UA_ECODE_REFUSED_MANAGEMENT_BLOCKING: case UA_ECODE_ASP_IDENTIFIER_REQUIRED: case UA_ECODE_INVALID_ASP_IDENTIFIER: case UA_ECODE_DESTINATION_STATUS_UNKNOWN: case UA_ECODE_ROUTING_KEY_CHANGE_REFUSED: case UA_ECODE_INVALID_LOADSHARING_LABEL: /* * M3UA would not work if this were true. Assume * that this is a bogus error. These error codes are * never expected on DATA. */ ptrace(("ERROR: Unexpected Error Code in ERR message for DATA\n")); return (-EPROTO); } return (-EFAULT); } /* * ASP ERR SSNM SCON * ------------------------------------------------------------------------- * We have received an error for a Nodal SCON that we have sent. We never * send nodal SCONs, so this is always a bogus error. */ static int asp_err_ssnm_scon(queue_t * q, mblk_t * mp, m3ua_parms_t * m, m3ua_parms_t * d) { return (-EFAULT); } /* * ASP ERR SSNM DAUD * ------------------------------------------------------------------------- */ static int asp_err_ssnm_daud(queue_t * q, mblk_t * mp, m3ua_parms_t * m, m3ua_parms_t * d) { return (-EFAULT); } /* * ASP ERR ASPS ASPUP REQ * ------------------------------------------------------------------------- */ static int asp_err_asps_aspup_req(queue_t * q, mblk_t * mp, m3ua_parms_t * m, m3ua_parms_t * d) { lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; if (xp->state == ASP_WACK_ASPUP) { /* * We will probably not see an acknowledgement now. We must * still diagnose the error. */ switch (m->common.ecode.val) { case UA_ECODE_INVALID_VERSION: /* * This is quite possible, particularly if this * is the very first ASPUP we have sent. This * can cause us some problems. We should no * longer attempt to bring this ASP up to that * SGP again. What we wil do is just place the * ASP in the DOWN state for that SGP and * inform management. */ case UA_ECODE_UNSUPPORTED_MESSAGE_CLASS: case UA_ECODE_UNSUPPORTED_MESSAGE_TYPE: case UA_ECODE_UNSUPPORTED_TRAFFIC_MODE: case UA_ECODE_PROTOCOL_ERROR: case UA_ECODE_INVALID_STREAM_IDENTIFIER: case UA_ECODE_REFUSED_MANAGEMENT_BLOCKING: case UA_ECODE_INVALID_PARAMETER_VALUE: case UA_ECODE_PARAMETER_FIELD_ERROR: if (xp->t_ack) untimeout(xchg(&xp->t_ack, 0)); xp->state = ASP_DOWN; fixme(("Inform Management\n")); return (0); case UA_ECODE_INVALID_ASP_IDENTIFIER: /* * We could retry without an ASP Id??? */ break; case UA_ECODE_ASP_IDENTIFIER_REQUIRED: /* * We could retry with an ASP Id??? */ break; case UA_ECODE_UNEXPECTED_MESSAGE: /* * This is one case mentioned in the specs; * however, the ASPUP_ACK is supposed to come * before this error message. There has maybe * just been a reversal. So we ignore this * message and fall back on timers. */ case UA_ECODE_UNEXPECTED_PARAMETER: /* * If we are supporting extensions and the * other end might not like our extension * parameters, we should reattempt with the * extension parameters suppressed. */ return (0); default: case UA_ECODE_DESTINATION_STATUS_UNKNOWN: case UA_ECODE_INVALID_NETWORK_APPEARANCE: case UA_ECODE_MISSING_PARAMETER: case UA_ECODE_ROUTING_KEY_CHANGE_REFUSED: case UA_ECODE_INVALID_ROUTING_CONTEXT: case UA_ECODE_NO_CONFIGURED_AS_FOR_ASP: case UA_ECODE_UNSUPPORTED_IID_TYPE: case UA_ECODE_INVALID_LOADSHARING_LABEL: case UA_ECODE_ASP_ACTIVE_FOR_IIDS: case UA_ECODE_UNASSIGNED_TEI: case UA_ECODE_UNRECOGNIZED_SAPI: case UA_ECODE_INVALID_TEI_SAPI_COMBINATION: case UA_ECODE_INVALID_IID: /* * These are bogus. */ } if (xp->t_ack) untimeout(xchg(&xp->t_ack, 0)); xp->state = ASP_DOWN; } return (0); } /* * ASP ERR ASPS ASPDN REQ * ------------------------------------------------------------------------- */ static int asp_err_asps_aspdn_req(queue_t * q, mblk_t * mp, m3ua_parms_t * m, m3ua_parms_t * d) { lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; if (xp->state == ASP_WACK_ASPDN) { /* * We will probably not see an acknowledgement now. We must * still diagnose the error. */ switch (m->common.ecode.val) { } if (xp->t_ack) untimeout(xchg(&xp->t_ack, 0)); xp->state = ASP_DOWN; } return (0); } /* * ASP ERR ASPS HBEAT REQ * ------------------------------------------------------------------------- */ static int asp_err_asps_hbeat_req(queue_t * q, mblk_t * mp, m3ua_parms_t * m, m3ua_parms_t * d) { lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; /* FIXME: maybe we have an RC in the HBEAT data??? */ if (xp->state == ASP_WACK_HBEAT) { } return (-EFAULT); } /* * ASP ERR ASPS HBEAT ACK * ------------------------------------------------------------------------- */ static int asp_err_asps_hbeat_ack(queue_t * q, mblk_t * mp, m3ua_parms_t * m, m3ua_parms_t * d) { /* not much to do here */ return (0); } /* * ASP ERR ASPT ASPAC REQ * ------------------------------------------------------------------------- */ static int asp_err_aspt_aspac_req(queue_t * q, mblk_t * mp, m3ua_parms_t * m, m3ua_parms_t * d) { lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; (void) xp; /* look up RC */ // if ( gp->state == AS_WACK_ASPAC ) { } return (-EFAULT); } /* * ASP ERR ASPT ASPIA REQ * ------------------------------------------------------------------------- */ static int asp_err_aspt_aspia_req(queue_t * q, mblk_t * mp, m3ua_parms_t * m, m3ua_parms_t * d) { lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; (void) xp; /* look up RC */ // if ( gp->state == AS_WACK_ASPIA ) { } return (-EFAULT); } /* * ASP ERR RKMM REG REQ * ------------------------------------------------------------------------- */ static int asp_err_rkmm_reg_req(queue_t * q, mblk_t * mp, m3ua_parms_t * m, m3ua_parms_t * d) { lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; (void) xp; /* look up loc-key-id */ // if ( gp->state == AS_WRSP_REG ) { } return (-EFAULT); } /* * ASP ERR RKMM DEREG REQ * ------------------------------------------------------------------------- */ static int asp_err_rkmm_dereg_req(queue_t * q, mblk_t * mp, m3ua_parms_t * m, m3ua_parms_t * d) { lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; (void) xp; /* look up RC */ // if ( gp->state == AS_WRSP_DEREG ) { } return (-EFAULT); } /* * SGP MGMT ERR * ------------------------------------------------------------------------- * We have received a MGMT ERR message at an ASP from an SGP. */ static int asp_mgmt_err(queue_t * q, mblk_t * mp) { uint32_t mhdr; int err = 0; m3ua_parms_t m = { 0, }, d = { 0,}; trace(); if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: Decode error in ERR message\n")); goto ua_noreply_err; } if (!m.common.ecode.u.wptr) { err = UA_ECODE_MISSING_PARAMETER; ptrace(("ERROR: Missing error code in ERR message\n")); goto ua_noreply_err; } if (m.common.ecode.len != 4) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: Parameter field error in ERR msg\n")); goto ua_noreply_err; } if ((err = m3ua_decode_diag(&m, &d))) { err = err; ptrace(("ERROR: Decode error in DIAG parameter\n")); goto ua_noreply_err; } switch (UA_MSG_CLAS((mhdr = m.common.diag.u.wptr[0]))) { case UA_CLASS_MGMT: switch (UA_MSG_TYPE(mhdr)) { default: case __UA_MSG_TYPE(UA_MGMT_ERR): case __UA_MSG_TYPE(UA_MGMT_NTFY): /* * We should not ever be sending these from * an ASP (or receiving an ERR on an ERR). */ break; } break; case UA_CLASS_XFER: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(M3UA_XFER_DATA): return asp_err_xfer_data(q, mp, &m, &d); } break; case UA_CLASS_SSNM: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_SSNM_SCON): return asp_err_ssnm_scon(q, mp, &m, &d); case __UA_MSG_TYPE(UA_SSNM_DAUD): return asp_err_ssnm_daud(q, mp, &m, &d); default: case __UA_MSG_TYPE(UA_SSNM_DUNA): case __UA_MSG_TYPE(UA_SSNM_DAVA): case __UA_MSG_TYPE(UA_SSNM_DUPU): case __UA_MSG_TYPE(UA_SSNM_DRST): /* * We should not ever be sending these from * an ASP. */ break; } break; case UA_CLASS_ASPS: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_ASPS_ASPUP_REQ): return asp_err_asps_aspup_req(q, mp, &m, &d); case __UA_MSG_TYPE(UA_ASPS_ASPDN_REQ): return asp_err_asps_aspdn_req(q, mp, &m, &d); case __UA_MSG_TYPE(UA_ASPS_HBEAT_REQ): return asp_err_asps_hbeat_req(q, mp, &m, &d); case __UA_MSG_TYPE(UA_ASPS_HBEAT_ACK): return asp_err_asps_hbeat_ack(q, mp, &m, &d); default: case __UA_MSG_TYPE(UA_ASPS_ASPUP_ACK): case __UA_MSG_TYPE(UA_ASPS_ASPDN_ACK): /* * We should not ever be sending these from * an ASP. */ break; } break; case UA_CLASS_ASPT: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_ASPT_ASPAC_REQ): return asp_err_aspt_aspac_req(q, mp, &m, &d); case __UA_MSG_TYPE(UA_ASPT_ASPIA_REQ): return asp_err_aspt_aspia_req(q, mp, &m, &d); default: case __UA_MSG_TYPE(UA_ASPT_ASPAC_ACK): case __UA_MSG_TYPE(UA_ASPT_ASPIA_ACK): /* * We should not ever be sending these from * an ASP. */ break; } break; case UA_CLASS_RKMM: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_RKMM_REG_REQ): return asp_err_rkmm_reg_req(q, mp, &m, &d); case __UA_MSG_TYPE(UA_RKMM_DEREG_REQ): return asp_err_rkmm_dereg_req(q, mp, &m, &d); default: case __UA_MSG_TYPE(UA_RKMM_REG_RSP): case __UA_MSG_TYPE(UA_RKMM_DEREG_RSP): /* * We should not ever be sending these from * an ASP. */ break; } break; default: case UA_CLASS_Q921: case UA_CLASS_MAUP: case UA_CLASS_CNLS: case UA_CLASS_CONS: case UA_CLASS_TDHM: case UA_CLASS_TCHM: /* * We should not ever be sending these from an M3UA ASP. */ break; } /* * We have no message in the Diagnostic information. Therefore, we * have to discern what is happening from our current state and from * information in the ERR message itself. */ if (m.common.rc.u.wptr) { /* * We have a Routing Context, so the message was an AS related message. */ } if (m.common.apc.u.wptr) { /* * We have an affected point code parameter, so the message was * an SSNM message. */ } if (m.common.tmode.u.wptr) { /* * We have a traffic mode parameter, so the message was either * an ASPT message or an RKMM message. */ } switch (m.common.ecode.val) { case UA_ECODE_INVALID_VERSION: case UA_ECODE_UNSUPPORTED_MESSAGE_CLASS: case UA_ECODE_UNSUPPORTED_MESSAGE_TYPE: case UA_ECODE_UNSUPPORTED_TRAFFIC_MODE: case UA_ECODE_UNEXPECTED_MESSAGE: case UA_ECODE_PROTOCOL_ERROR: case UA_ECODE_INVALID_STREAM_IDENTIFIER: case UA_ECODE_REFUSED_MANAGEMENT_BLOCKING: case UA_ECODE_ASP_IDENTIFIER_REQUIRED: case UA_ECODE_INVALID_ASP_IDENTIFIER: case UA_ECODE_INVALID_PARAMETER_VALUE: case UA_ECODE_PARAMETER_FIELD_ERROR: case UA_ECODE_UNEXPECTED_PARAMETER: case UA_ECODE_DESTINATION_STATUS_UNKNOWN: case UA_ECODE_INVALID_NETWORK_APPEARANCE: case UA_ECODE_MISSING_PARAMETER: case UA_ECODE_ROUTING_KEY_CHANGE_REFUSED: case UA_ECODE_INVALID_ROUTING_CONTEXT: case UA_ECODE_NO_CONFIGURED_AS_FOR_ASP: case UA_ECODE_UNSUPPORTED_IID_TYPE: case UA_ECODE_INVALID_LOADSHARING_LABEL: case UA_ECODE_ASP_ACTIVE_FOR_IIDS: case UA_ECODE_UNASSIGNED_TEI: case UA_ECODE_UNRECOGNIZED_SAPI: case UA_ECODE_INVALID_TEI_SAPI_COMBINATION: case UA_ECODE_INVALID_IID: } err = UA_ECODE_INVALID_PARAMETER_VALUE; ptrace(("ERROR: Invalid parameter in ERR message\n")); ua_noreply_err: /* never return an error to an error message */ return ua_noreply_err(q, err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * MGMT NTFY: * ------------------------------------------------------------------------- * IMPLEMENTATION NOTES:- Notifications are advisory only. In no situation * are we required by the specification to take action on a Notification. In * some circumstances we may wish to change state information with respect to * an ASP or an AS on the basis of a notification. * * Notifications are advisory and can effectively be ignored. There is never * an instance where an ASP is required to act on a Notify message. One * exception might be for override AS when ALTERNATE_ASP_ACTIVE is received. * The currently AS-ACTIVE AS should be placed into the AS-INACTIVE state. */ static int asp_mgmt_ntfy(queue_t * q, mblk_t * mp) { int err = 0; lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; gp_t *gp; as_t *as; m3ua_parms_t m = { 0, }; trace(); if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: Decoding error\n")); goto ua_reply_error; } if (!m.common.rc.u.wptr && (xp->asat_count == 1)) { /* missing routing context: use default */ static uint32_t rc; rc = htonl(xp->gp->rc); m.common.rc.u.wptr = &rc; m.common.rc.len = 4; m.common.rc.val = xp->gp->rc; } if (!m.common.rc.u.wptr) { err = UA_ECODE_NO_CONFIGURED_AS_FOR_ASP; ptrace(("ERROR: missing RC parm\n")); goto ua_reply_error; } if (m.common.rc.len != 4) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad RC parm len = %d\n", m.common.rc.len)); goto ua_reply_error; } for (gp = xp->gp; gp; gp = gp->as.next) if (gp->rc == m.common.rc.val) break; if (!gp || !(as = gp->as.as)) { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: invalid RC parm\n")); goto ua_reply_error; } switch (m.common.status.val) { case UA_STATUS_AS_INACTIVE: /* may want to attempt to resync AS state */ return (0); /* XXX ignore for now */ return ua_ntfy_as_change(q, as, AS_INACTIVE); case UA_STATUS_AS_ACTIVE: /* may want to attempt to resync AS state */ return (0); /* XXX ignore for now */ return ua_ntfy_as_change(q, as, AS_ACTIVE); case UA_STATUS_AS_PENDING: /* may want to attempt to resync AS state */ return (0); /* XXX ignore for now */ return ua_ntfy_as_change(q, as, AS_PENDING); case UA_STATUS_AS_INSUFFICIENT_ASPS: /* what to do here? Notify management? */ return (0); case UA_STATUS_ALTERNATE_ASP_ACTIVE: /* this is only valid for an override AS */ return (0); /* XXX ignore for now */ return ua_ntfy_as_change(q, as, AS_INACTIVE); case UA_STATUS_ASP_FAILURE: /* FIXME: should this have an RC? */ /* what to do here? Notify management? */ if (!m.common.aspid.u.wptr) { err = UA_ECODE_ASP_IDENTIFIER_REQUIRED; ptrace(("ERROR: missing ASPID\n")); goto ua_reply_error; } if (m.common.aspid.len != 4) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad ASPID\n")); goto ua_reply_error; } /* FIXME: need to check ASPId for validity */ return (0); case UA_STATUS_AS_MINIMUM_ASPS: /* what to do here? Notify management? */ return (0); default: case UA_STATUS_AS_DOWN: /* this is an error now */ err = UA_ECODE_INVALID_PARAMETER_VALUE; ptrace(("ERROR: old ASP_DOWN in NTFY\n")); goto ua_reply_error; } ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * XFER DATA: * ------------------------------------------------------------------------- * IMPLEMENTATION NOTES:- We deliver the DATA by Routing Context. First we * find the AS-P for the Routing Context, and then locate the ASP or SS7-U. * Further processing of the message is handled by the ASP or SS7-U. * * The specs do not really say what to do with messages received for an * inactive AS. We return the message in an ERR("Unexpected Message"). We * should probably also send back an ASPIA Request. */ static int asp_xfer_data(queue_t * q, mblk_t * mp) { /* */ int err = 0; gp_t *gp; as_t *as; queue_t *outq; m3ua_parms_t m = { 0, }; xp_t *xp = ((ua_t *) q->q_ptr)->xp; trace(); if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: decoding error\n")); goto ua_reply_error; } if (m.prot_data.u.wptr) { err = UA_ECODE_MISSING_PARAMETER; ptrace(("ERROR: missing protocol data\n")); goto ua_reply_error; } if (!m.common.rc.u.wptr && (xp->asat_count == 1)) { /* missing routing context: use default */ static uint32_t rc; rc = htonl(xp->gp->rc); m.common.rc.u.wptr = &rc; m.common.rc.len = 4; m.common.rc.val = xp->gp->rc; } if (!m.common.rc.u.wptr) { err = UA_ECODE_NO_CONFIGURED_AS_FOR_ASP; ptrace(("ERROR: missing routing context\n")); goto ua_reply_error; } if (m.common.rc.len != 4) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad routing context\n")); goto ua_reply_error; } if (m.prot_data.len <= 12) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad protocol data\n")); goto ua_reply_error; } /* try to find the routing context */ for (gp = xp->gp; gp; gp = gp->as.next) if (gp->rc == m.common.rc.val) break; if (!gp || !(as = gp->as.as)) { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: invalid routing context\n")); goto ua_reply_error; } switch (as->state) { case AS_ACTIVE: break; default: case AS_DOWN: case AS_WACK_ASPUP: case AS_WACK_ASPDN: case AS_UNREG: case AS_WRSP_REG: case AS_WRSP_DEREG: case AS_BLOCKED: case AS_INACTIVE: case AS_WACK_ASPIA: case AS_WACK_ASPAC: case AS_PENDING: case AS_WACK_HBEAT: err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: invalid routing context\n")); goto ua_reply_error; } if (!(outq = m3ua_asp_route(as, &m, &err))) { err = err; ptrace(("ERROR: couldn't route message\n")); goto ua_reply_error; } putq(outq, mp); return (QR_TRIMMED); ua_reply_error: /* we could also kick the other side with an ASPIA ACK */ return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * SSNM * ------------------------------------------------------------------------- */ static int asp_ssnm(queue_t * q, mblk_t * mp) { /* * We deliver the SSNM by Routing Context. First we find the AS-P for * each Routing Context, and then locate each ASP. Further processing * of a copy of the message is handled by the ASP or SS7-User. */ int i, err = 0; m3ua_parms_t m = { 0, }; xp_t *xp = ((ua_t *) q->q_ptr)->xp; trace(); if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: decoding error\n")); goto ua_reply_error; } if (!m.common.rc.u.wptr && (xp->asat_count == 1)) { /* missing routing context: use default */ static uint32_t rc; rc = htonl(xp->gp->rc); m.common.rc.u.wptr = &rc; m.common.rc.len = 4; m.common.rc.val = xp->gp->rc; } if (!m.common.rc.u.wptr) { err = UA_ECODE_NO_CONFIGURED_AS_FOR_ASP; ptrace(("ERROR: missing routing context\n")); goto ua_reply_error; } if (m.common.rc.len < 4 || (m.common.rc.len & 0x3)) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad routing context\n")); goto ua_reply_error; } if (!m.common.apc.u.wptr) { err = UA_ECODE_MISSING_PARAMETER; ptrace(("ERROR: missing APC\n")); goto ua_reply_error; } if (m.common.apc.len < 4 && (m.common.apc.len & 0x3)) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad APC\n")); goto ua_reply_error; } if (UA_MSG_TYPE(m.type) == UA_SSNM_DUPU && !m.user_cause.u.wptr) { err = UA_ECODE_MISSING_PARAMETER; ptrace(("ERROR: missing cause\n")); goto ua_reply_error; } if (UA_MSG_TYPE(m.type) == UA_SSNM_DUPU && m.user_cause.len != 4) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad cause\n")); goto ua_reply_error; } if (UA_MSG_TYPE(m.type) == UA_SSNM_SCON && !m.cong_ind.u.wptr) { err = UA_ECODE_MISSING_PARAMETER; ptrace(("ERROR: missing cong ind\n")); goto ua_reply_error; } if (UA_MSG_TYPE(m.type) == UA_SSNM_SCON && m.cong_ind.len != 4) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad cong ind\n")); goto ua_reply_error; } /* for each routing context */ for (i = 0; i < (m.common.rc.len >> 2); i++) { gp_t *gp; uint32_t rc = ntohl(m.common.rc.u.wptr[i]); err = 0; for (gp = xp->gp; gp; gp = gp->as.next) if (gp->rc == rc) break; if (!gp) { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: invalid routing context\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_INACTIVE | ASF_WACK_ASPAC)) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: AS is inactive\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_DOWN | ASF_WACK_ASPUP | ASF_WACK_ASPDN)) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: AS is down\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_UNREG | ASF_WRSP_REG | ASF_WRSP_DEREG)) { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: AS not registered\n")); goto ua_reply_error_loop; } if ((1 << gp->state) & (ASF_BLOCKED)) { err = UA_ECODE_REFUSED_MANAGEMENT_BLOCKING; ptrace(("ERROR: ASP management blocked\n")); goto ua_reply_error_loop; } { as_t *ps = gp->as.as; ap_t *ap; if ((err = asp_update_routes(ps, &m))) return (err); for (ap = ps->ap; ap; ap = ap->prov.next) { as_t *as = ap->user.as; gp_t *gp; for (gp = as->gp; gp; gp = gp->as.next) { if ((1 << gp->state) & (ASM_ACTV_STATES)) { mblk_t *dp; xp_t *xp = gp->xp.xp; lp_t *lp = xp->pp.pp; if ((dp = dupmsg(mp))) { putq(lp->wq, dp); continue; } rare(); return ua_bufcall(q, 0, BPRI_MED); } } } } ua_reply_error_loop: if (err && (err = ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr + i, m.common.apc.u.wptr, m.na.u.wptr, mp))) return (err); } return (0); ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * ASPS ASPUP ACK * ------------------------------------------------------------------------- */ static int asp_asps_aspup_ack(queue_t * q, mblk_t * mp) { int err; xp_t *xp = ((lp_t *) q->q_ptr)->xp; gp_t *gp; m3ua_parms_t m = { 0, }; if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: decoding error\n")); goto ua_reply_error; } if (xp->state != ASP_WACK_ASPUP) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: unexpected message\n")); goto ua_reply_error; } /* * This ASP was expecting an ASPUP Ack. The ASP needs to automatically * register all AS that need registration and automatically activate all AS * that need activation. To accomplish this we loop thru all of the AS that * are associated with this ASP which has just come up. */ for (gp = xp->gp; gp; gp = gp->as.next) { as_t *ps = gp->as.as; as_t *as = ps->ap->user.as; xp->asdn_count--; ps->xpdn_count--; if (gp->flags & AS_DYNAMIC) { /* register */ if ((err = m3ua_send_reg_req(OTHER(q), as))) return (err); xp->asur_count++; ps->xpur_count++; gp->state = AS_WRSP_REG; } else { /* activate */ if ((err = ua_send_aspac_req(q, OTHER(q), as->tmode, as->rc))) return (err); xp->asia_count++; ps->xpia_count++; gp->state = AS_WACK_ASPAC; } if (xp->t_ack) untimeout(xchg(&xp->t_ack, 0)); xp->state = ASP_UP; return (0); } ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * ASPS ASPDN ACK * ------------------------------------------------------------------------- * This ASPDN Ack may be solicited (we are in the ASP_WACK_ASPDN state) or * unsolicited (we are in another state). We never return an unexpected * message error to an ASPDN Ack. */ static int asp_asps_aspdn_ack(queue_t * q, mblk_t * mp) { int err = 0; m3ua_parms_t m = { 0, }; xp_t *xp = ((ua_t *) q->q_ptr)->xp; gp_t *gp; trace(); if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: decode error\n")); goto ua_reply_error; } if (!xp) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: no XP attached\n")); goto ua_reply_error; } if ((1 << xp->state) & (ASM_DOWN_STATES | ASM_UREG_STATES)) { xp->state = ASP_DOWN; return (0); } if ((1 << xp->state) & ~(ASM_INAC_STATES | ASM_ACTV_STATES)) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: XP in bad state\n")); goto ua_reply_error; } for (gp = xp->gp; gp; gp = gp->xp.next) { xp_t *xp = gp->xp.xp; as_t *ps = gp->as.as; if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); if (gp->t_hbt) untimeout(xchg(&gp->t_hbt, 0)); switch (gp->state) { case AS_WACK_ASPDN: case AS_WACK_ASPUP: case AS_DOWN: break; case AS_UNREG: case AS_WRSP_REG: case AS_WRSP_DEREG: if (ps->xpur_count == 1 && !ps->xpac_count && !ps->xpia_count) { if ((err = sg_down_ack(q, ps)) < 0) return (err); if (err > 0) goto ua_reply_error; } xp->asur_count--; ps->xpur_count--; break; case AS_INACTIVE: case AS_WACK_ASPAC: /* this is the last inactive ASP for the AS-P */ if (ps->xpia_count == 1 && !ps->xpac_count) { if ((err = sg_down_ack(q, ps)) < 0) return (err); if (err > 0) goto ua_reply_error; } xp->asia_count--; ps->xpia_count--; break; case AS_ACTIVE: case AS_WACK_ASPIA: case AS_WACK_HBEAT: /* this is the last active ASP for the AS-P */ if (ps->xpac_count == 1) { if ((err = sg_down_ack(q, ps)) < 0) return (err); if (err > 0) goto ua_reply_error; } xp->asac_count--; ps->xpac_count--; break; case AS_PENDING: case AS_BLOCKED: continue; default: never(); ptrace(("SWERR: GP in unexpected state\n")); continue; } xp->asdn_count++; as->xpdn_count++; gp->state = AS_DOWN; } if (xp->t_ack) untimeout(xchg(&xp->t_ack, 0)); if (xp->t_hbt) untimeout(xchg(&xp->t_hbt, 0)); xp->state = ASP_DOWN; /* * FIXME: we need to start trying to move the XP back * to the ASP_UP state if this was an unsolicited * ASPDN Ack. */ return (0); ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * ASPS BEAT * ------------------------------------------------------------------------- */ static int asp_asps_hbeat_req(queue_t * q, mblk_t * mp) { int err = 0; m3ua_parms_t m = { 0, }; xp_t *xp = ((ua_t *) q->q_ptr)->xp; trace(); if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: decoding error\n")); goto ua_reply_error; } if (xp->state == ASP_DOWN) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: BEAT in ASP_DOWN\n")); goto ua_reply_error; } return ua_send_hbeat_ack(OTHER(q), m.common.hbdata.u.cptr, m.common.hbdata.len); ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * ASPS BEAT ACK * ------------------------------------------------------------------------- */ static int asp_asps_hbeat_ack(queue_t * q, mblk_t * mp) { int err = 0; m3ua_parms_t m = { 0, }; xp_t *xp = ((ua_t *) q->q_ptr)->xp; trace(); if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: decoding error\n")); goto ua_reply_error; /* decoding error */ } if (xp->state == ASP_WACK_HBEAT) { if (xp->t_hbt) untimeout(xchg(&xp->t_hbt, 0)); xp->state = ASP_ACTIVE; } /* discard for now... */ return (0); ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * ASPT ASPAC ACK * ------------------------------------------------------------------------- */ static int asp_aspt_aspac_ack(queue_t * q, mblk_t * mp) { int i, err = 0; m3ua_parms_t m = { 0, }; xp_t *xp = ((ua_t *) q->q_ptr)->xp; trace(); if ((1 << xp->state) & (ASPF_DOWN | ASPF_WACK_ASPUP | ASPF_WACK_ASPDN)) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: ASP is down\n")); goto ua_reply_error; } if (xp->state == ASP_BLOCKED) { err = UA_ECODE_REFUSED_MANAGEMENT_BLOCKING; ptrace(("ERROR: ASP is blocked\n")); goto ua_reply_error; } if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: message decode error\n")); goto ua_reply_error; } if (!m.common.rc.u.wptr && (xp->asat_count == 1)) { /* missing routing context: use default */ static uint32_t rc; rc = htonl(xp->gp->rc); m.common.rc.u.wptr = &rc; m.common.rc.len = 4; m.common.rc.val = xp->gp->rc; } if (!m.common.rc.u.wptr) { err = UA_ECODE_NO_CONFIGURED_AS_FOR_ASP; ptrace(("ERROR: missing routing context\n")); goto ua_reply_error; } if (m.common.rc.len < 4 || (m.common.rc.len & 0x3)) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad routing context\n")); goto ua_reply_error; } for (i = 0; i < (m.common.rc.len >> 2); i++) { gp_t *gp; uint32_t rc = htonl(*(m.common.rc.u.wptr + i)); err = 0; for (gp = xp->gp; gp; gp = gp->as.next) if (gp->rc == rc) break; if (gp) { if ((err = sg_active_ack(q, gp->as.as))) return (err); gp->state = AS_ACTIVE; } else { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: invalid routing context\n")); } if (err && (err = ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr + i, NULL, NULL, mp))) return (err); } return (0); ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * ASPT ASPIA ACK * ------------------------------------------------------------------------- */ static int asp_aspt_aspia_ack(queue_t * q, mblk_t * mp) { int i, err = 0; xp_t *xp = ((ua_t *) q->q_ptr)->xp; m3ua_parms_t m = { 0, }; trace(); if ((1 << xp->state) & (ASM_DOWN_STATES)) { err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: ASP is down\n")); goto ua_reply_error; } if (xp->state == ASP_BLOCKED) { err = UA_ECODE_REFUSED_MANAGEMENT_BLOCKING; ptrace(("ERROR: ASP is blocked\n")); goto ua_reply_error; } if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: message decode error\n")); goto ua_reply_error; } if (!m.common.rc.u.wptr && (xp->asat_count == 1)) { /* missing routing context: use default */ static uint32_t rc; rc = htonl(xp->gp->rc); m.common.rc.u.wptr = &rc; m.common.rc.len = 4; m.common.rc.val = xp->gp->rc; } if (!m.common.rc.u.wptr) { err = UA_ECODE_NO_CONFIGURED_AS_FOR_ASP; ptrace(("ERROR: missing routing context\n")); goto ua_reply_error; } if (m.common.rc.len < 4 && (m.common.rc.len & 0x3)) { err = UA_ECODE_PARAMETER_FIELD_ERROR; ptrace(("ERROR: bad routing context\n")); goto ua_reply_error; } for (i = 0; i < (m.common.rc.len >> 2); i++) { gp_t *gp; uint32_t rc = htonl(*(m.common.rc.u.wptr + i)); err = 0; for (gp = xp->gp; gp; gp = gp->as.next) if (gp->rc == rc) break; if (gp) { xp_t *xp = gp->xp.xp; as_t *ps = gp->as.as; (void) xp; switch (gp->state) { case AS_WACK_ASPUP: case AS_WACK_ASPDN: case AS_DOWN: case AS_WRSP_REG: case AS_WRSP_DEREG: case AS_UNREG: case AS_PENDING: case AS_BLOCKED: default: err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("Received ASPIA Ack in unexpcted state\n")); break; case AS_INACTIVE: case AS_WACK_ASPAC: case AS_WACK_HBEAT: case AS_WACK_ASPIA: case AS_ACTIVE: if ((err = sg_inactive_ack(q, ps))) { if (err < 0) return (err); else break; } gp->state = AS_INACTIVE; /* cancel activation request */ if (ps->xpar_count == 1) { as_t *as = ps->ap->user.as; if (as->spar_count == 1) { as->state = AS_INACTIVE; } as->spar_count--; ps->state = AS_INACTIVE; } ps->xpar_count--; gp->state = AS_INACTIVE; continue; } if ((err = sg_inactive_ack(q, gp->as.as))) return (err); gp->state = AS_INACTIVE; } else { err = UA_ECODE_INVALID_ROUTING_CONTEXT; ptrace(("ERROR: invalid routing context\n")); } if (err && (err = ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, NULL, NULL, mp))) return (err); } return (0); ua_reply_error: return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * RKMM REG RSP * ------------------------------------------------------------------------- */ static int asp_rkmm_reg_rsp(queue_t * q, mblk_t * mp) { int err; lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; gp_t *gp; as_t *as; m3ua_parms_t m = { 0, }; trace(); if ((err = m3ua_decode_parms(mp, &m))) { ptrace(("ERROR: message decode error\n")); goto ua_reply_error; } if (!xp) { return (-EAGAIN); } for (gp = xp->gp; gp; gp = gp->as.next) { as = gp->as.as; if ((gp->state == ASP_WRSP_REG) && (as->rc == m.loc_key_id.val)) { fixme(("Process the reg rsp\n")); return (0); } } err = UA_ECODE_UNEXPECTED_MESSAGE; ptrace(("ERROR: unexpected message\n")); goto ua_reply_error; ua_reply_error: seldom(); return ua_reply_err(q, OTHER(q), err, m.common.rc.u.wptr, m.common.apc.u.wptr, m.na.u.wptr, mp); } /* * RKMM DEREG RSP * ------------------------------------------------------------------------- */ static int asp_rkmm_dereg_rsp(queue_t * q, mblk_t * mp) { trace(); fixme(("Write this function\n")); return (-EFAULT); } /* * ------------------------------------------------------------------------- * * ASP Message Handling * * ------------------------------------------------------------------------- */ static int asp_msg(queue_t * q, mblk_t * pdu) { int err; mblk_t *mp = pdu->b_cont; uint32_t mhdr = *((uint32_t *) mp->b_rptr); switch (UA_MSG_CLAS(mhdr)) { case UA_CLASS_MGMT: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_MGMT_ERR): return asp_mgmt_err(q, mp); case __UA_MSG_TYPE(UA_MGMT_NTFY): return asp_mgmt_ntfy(q, mp); } ptrace(("Unexpected MGMT message\n")); goto asp_msg_unsupported_message_type; case UA_CLASS_XFER: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(M3UA_XFER_DATA): return asp_xfer_data(q, mp); } ptrace(("Unexpected XFER message\n")); goto asp_msg_unsupported_message_type; case UA_CLASS_SSNM: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_SSNM_DUNA): case __UA_MSG_TYPE(UA_SSNM_DAVA): case __UA_MSG_TYPE(UA_SSNM_SCON): case __UA_MSG_TYPE(UA_SSNM_DUPU): case __UA_MSG_TYPE(UA_SSNM_DRST): return asp_ssnm(q, mp); case __UA_MSG_TYPE(UA_SSNM_DAUD): break; } ptrace(("Unexpected SSNM message\n")); goto asp_msg_unsupported_message_type; case UA_CLASS_ASPS: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_ASPS_ASPUP_REQ): break; case __UA_MSG_TYPE(UA_ASPS_ASPDN_REQ): break; case __UA_MSG_TYPE(UA_ASPS_ASPUP_ACK): return asp_asps_aspup_ack(q, mp); case __UA_MSG_TYPE(UA_ASPS_ASPDN_ACK): return asp_asps_aspdn_ack(q, mp); case __UA_MSG_TYPE(UA_ASPS_HBEAT_REQ): return asp_asps_hbeat_req(q, mp); case __UA_MSG_TYPE(UA_ASPS_HBEAT_ACK): return asp_asps_hbeat_ack(q, mp); } ptrace(("Unexpected ASPS message\n")); goto asp_msg_unsupported_message_type; case UA_CLASS_ASPT: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_ASPT_ASPAC_REQ): break; case __UA_MSG_TYPE(UA_ASPT_ASPIA_REQ): break; case __UA_MSG_TYPE(UA_ASPT_ASPAC_ACK): return asp_aspt_aspac_ack(q, mp); case __UA_MSG_TYPE(UA_ASPT_ASPIA_ACK): return asp_aspt_aspia_ack(q, mp); } ptrace(("Unexpected ASPT message\n")); goto asp_msg_unsupported_message_type; case UA_CLASS_Q921: ptrace(("Unexpected Q921 message\n")); goto asp_msg_unsupported_message_class; case UA_CLASS_MAUP: ptrace(("Unexpected MAUP message\n")); goto asp_msg_unsupported_message_class; case UA_CLASS_CNLS: ptrace(("Unexpected CNLS message\n")); goto asp_msg_unsupported_message_class; case UA_CLASS_CONS: ptrace(("Unexpected CONS message\n")); goto asp_msg_unsupported_message_class; case UA_CLASS_RKMM: switch (UA_MSG_TYPE(mhdr)) { case __UA_MSG_TYPE(UA_RKMM_REG_REQ): break; case __UA_MSG_TYPE(UA_RKMM_DEREG_REQ): break; case __UA_MSG_TYPE(UA_RKMM_REG_RSP): return asp_rkmm_reg_rsp(q, mp); case __UA_MSG_TYPE(UA_RKMM_DEREG_RSP): return asp_rkmm_dereg_rsp(q, mp); } ptrace(("Unexpected RKMM message\n")); goto asp_msg_unsupported_message_type; case UA_CLASS_TDHM: ptrace(("Unexpected TDHM message\n")); goto asp_msg_unsupported_message_class; case UA_CLASS_TCHM: ptrace(("Unexpected TCHM message\n")); goto asp_msg_unsupported_message_class; default: ptrace(("Unexpected message class %d\n", UA_MSG_CLAS(mhdr))); goto asp_msg_unsupported_message_class; } asp_msg_unsupported_message_type: if ((err = ua_reply_err(q, OTHER(q), UA_ECODE_UNSUPPORTED_MESSAGE_TYPE, NULL, NULL, NULL, mp))) return (err); return (-EPROTO); asp_msg_unsupported_message_class: if ((err = ua_reply_err(q, OTHER(q), UA_ECODE_UNSUPPORTED_MESSAGE_CLASS, NULL, NULL, NULL, mp))) return (err); return (-EPROTO); } /* * N_DATA_IND * ------------------------------------------------------------------------- */ static int asp_data_ind(queue_t * q, mblk_t * mp) { return asp_msg(q, mp); } /* * N_EXDATA_IND * ------------------------------------------------------------------------- */ static int asp_exdata_ind(queue_t * q, mblk_t * mp) { return asp_msg(q, mp); } /* * N_RESET_IND * ------------------------------------------------------------------------- */ static int asp_reset_ind(queue_t * q, mblk_t * mp) { fixme(("Write this function\n")); return (-EFAULT); } /* * N_DISCON_IND * ------------------------------------------------------------------------- */ static int asp_discon_ind(queue_t * q, mblk_t * mp) { fixme(("Write this function\n")); return (-EFAULT); } /* * M_PROTO Handling * ------------------------------------------------------------------------- */ static int asp_r_proto(queue_t * q, mblk_t * mp) { switch (*((long *) mp->b_rptr)) { case N_DATA_IND: return asp_data_ind(q, mp); case N_EXDATA_IND: return asp_exdata_ind(q, mp); case N_DISCON_IND: return asp_discon_ind(q, mp); case N_RESET_IND: return asp_reset_ind(q, mp); } return (-EOPNOTSUPP); } static int asp_r_error(queue_t * q, mblk_t * mp) { (void) q; (void) mp; fixme(("write this function\n")); return (-EFAULT); } static int asp_r_hangup(queue_t * q, mblk_t * mp) { int err = 0; xp_t *xp = ((ua_t *) q->q_ptr)->xp; gp_t *gp; if (!xp) return (-EFAULT); if ((1 << xp->state) & (ASM_DOWN_STATES | ASM_UREG_STATES)) { xp->state = ASP_DOWN; return (0); } if ((1 << xp->state) & ~(ASM_INAC_STATES | ASM_ACTV_STATES)) { return (-EFAULT); } /* for each of the AS associated with this SGP */ for (gp = xp->gp; gp; gp = gp->next) { xp_t *xp = gp->xp.xp; as_t *ps = gp->as.as; if (gp->t_ack) untimeout(xchg(&gp->t_ack, 0)); if (gp->t_hbt) untimeout(xchg(&gp->t_hbt, 0)); switch (gp->state) { case AS_DOWN: case AS_WACK_ASPDN: case AS_WACK_ASPUP: break; case AS_UNREG: case AS_WRSP_REG: case AS_WRST_DEREG: /* this is last registered SGP for the AS */ if (ps->xpur_count == 1 && !ps->xpia_count && !ps->xpac_count) if ((err = sg_down_ack(q, ps)) < 0) return (err); xp->asur_count--; xp->xpur_count--; break; case AS_INACTIVE: case AS_WACK_ASPAC: /* this is last inactive SGP for the AS */ if (ps->xpia_count == 1 && !ps->xpac_count) if ((err = sg_down_ack(q, ps)) < 0) return (err); xp->asia_count--; ps->xpia_count--; break; case AS_ACTIVE: case AS_WACK_ASPIA: case AS_WACK_HBEAT: /* this is last active SGP for the AS */ if (ps->xpac_count == 1) if ((err = sg_down_ack(q, ps)) < 0) return (err); xp->asac_count--; as->xpac_count--; break; case AS_PENDING: case AS_BLOCKED: continue; default: never(); ptrace(("SWERR: GP in unexpected state\n")); } xp->asdn_count++; xp->xpdn_count++; gp->state = AS_DOWN; } return (0); } static int asp_r_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_PROTO: case M_PCPROTO: return asp_r_proto(q, mp); case M_ERROR: return asp_r_error(q, mp); case M_HANGUP: return asp_r_hangup(q, mp); case M_FLUSH: return ua_r_flush(q, mp); } return (-EOPNOTSUPP); } /* * M3UA (ASP) --> SCTP NPI Transport Primitives * ------------------------------------------------------------------------- * This set of functions handle sending M3UA primitives from an ASP * (Application Server Process) to the underlying SCTP Transport provider * utilizing the NPI (Network Provider Interface). These messages are already * prepared M_DATA or M_CTL M3UA messages or they are control messages * (M_ERROR, M_HANGUP, M_FLUSH) or they are already formatted M_PROTO and * M_PCPROTO primitives. These handling functions take the M_DATA and M_CTL * messages and formulate an NPI primitive to SCTP with the appropriate SCTP * stream and other information filled out. */ /* * M_DATA Handling * ------------------------------------------------------------------------- * When an M_DATA message is found on the write queue to the SGP, we just tag * on a N_DATA_REQ primitive and QOS M_PROTO block and put the message back on * the queue. Anything sent as an M_DATA is not worth expediting. These are * sent as N_DATA_REQ in band 0. * * IMPLEMENTATION NOTES:- MGMT, RKMM and ASPS (including BEAT) and SSNM can be * sent ordered. */ static int asp_w_data(queue_t * q, mblk_t * pdu) { mblk_t *mp; N_data_req_t *p; N_qos_sel_data_sctp_t *qos; size_t mlen = sizeof(*p) + sizeof(*qos); if ((mp = ua_allocb(q, mlen, BPRI_MED))) { uint32_t mhdr = *((uint32_t *) pdu->b_rptr); mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_DATA_REQ; p->DATA_xfer_flags = 0; qos = ((typeof(qos)) mp->b_wptr)++; qos->n_qos_type = N_QOS_SEL_DATA_SCTP; qos->ppi = M3UA_PPI; qos->tsn = 0; /* not used on request */ qos->ssn = 0; /* not used on request */ qos->more = 0; /* always sent complete */ mp->b_cont = pdu; switch (UA_MSG_CLAS(mhdr)) { case UA_CLASS_MGMT: case UA_CLASS_ASPS: case UA_CLASS_RKMM: qos->sid = 0; /* always sent on stream '0' */ break; case UA_CLASS_ASPT: case UA_CLASS_XFER: { /* * Calculate stream id using Routing Context * and an SLS value of zero (it is not * necessary to loadshare SSNM over SLS because * there are no per-stream flow controls yet. * When we put per-stream flow controls in * place, we should loadshare over SLS values. * * IMPLEMENTATION NOTE:- We actually never send * SSNM ordered. */ uint32_t rc = ntohl(((uint32_t *) pdu->b_rptr)[3]); fixme(("Map RC onto a stream id\n")); qos->sid = rc; break; } case UA_CLASS_SSNM: { /* * Calculate stream id using Routing Context * and an SLS value of zero (it is not * necessary to loadshare SSNM over SLS because * there are no per-stream flow controls yet. * When we put per-stream flow controls in * place, we should loadshare over SLS values. * * IMPLEMENTATION NOTE:- We actually never send * SSNM ordered. */ uint32_t rc = ntohl(((uint32_t *) pdu->b_rptr)[3]); (void) rc; fixme(("do something here\n")); break; } default: case UA_CLASS_MAUP: case UA_CLASS_Q921: case UA_CLASS_CNLS: case UA_CLASS_CONS: case UA_CLASS_TDHM: case UA_CLASS_TCHM: never(); freeb(mp); return (-EFAULT); } putnext(q, mp); return (QR_ABSORBED); /* we have absorbed the data blocks */ } return (-ENOBUFS); } /* * M_CTL Handling * ------------------------------------------------------------------------- * When a M_CTL is found on the write queue to the SGP, we just tag on a * N_EXDATA_REQ primitive and QOS M_PROTO block and put the message back on the * queue. Anything sent as an M_CTL is worth expediting. These are sent as * N_EXDATA_REQ in band 1. * * IMPLEMENTATION NOTES:- MGMT, RKMM, ASPS (including BEAT) and SNMM messages * may be sent unordered and expedited. These messages are always sent on * stream '0' for SCTP when expedited. All other message classes should be * sent ordered. BEAT may be sent ordered or unordered. */ static int asp_w_ctl(queue_t * q, mblk_t * pdu) { mblk_t *mp; N_exdata_req_t *p; N_qos_sel_data_sctp_t *qos; size_t mlen = sizeof(*p) + sizeof(*qos); if ((mp = ua_allocb(q, mlen, BPRI_MED))) { uint32_t mhdr = *((uint32_t *) pdu->b_rptr); mp->b_datap->db_type = M_PROTO; mp->b_band = 1; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_EXDATA_REQ; qos = ((typeof(qos)) mp->b_wptr)++; qos->n_qos_type = N_QOS_SEL_DATA_SCTP; qos->ppi = M3UA_PPI; qos->tsn = 0; /* not used on request */ qos->ssn = 0; /* not used on request */ qos->more = 0; /* always sent complete */ mp->b_cont = pdu; pdu->b_datap->db_type = M_DATA; switch (UA_MSG_CLAS(mhdr)) { /* * These messages are always sent expedited on stream '0'. */ case UA_CLASS_MGMT: case UA_CLASS_RKMM: case UA_CLASS_ASPS: qos->sid = 0; /* always sent on stream '0' */ break; /* * SSNM message should be sent unordered and expedited; * however, they should be sent on the stream which corresponds * to the Routing Context in the message. */ case UA_CLASS_SSNM: { /* * Calculate stream id using Routing Context and an SLS * value of zero (it is not necessary to loadshare SSNM * over SLS because there are no per-stream flow * controls yet. TODO: When we put per-stream flow * controls in place, we should load-share SSNM over * SLS values. */ /* rc always at fixed location in message */ uint32_t rc = ntohl(((uint32_t *) pdu->b_rptr)[3]); (void) rc; fixme(("We need to know number of streams to calculate sid\n")); qos->sid = 1; break; } /* * These messages are never sent expedited. */ default: case UA_CLASS_ASPT: case UA_CLASS_XFER: case UA_CLASS_MAUP: case UA_CLASS_Q921: case UA_CLASS_CNLS: case UA_CLASS_CONS: case UA_CLASS_TDHM: case UA_CLASS_TCHM: never(); freeb(mp); return (-EFAULT); } putnext(q, mp); return (QR_ABSORBED); /* we have absorbed the data blocks */ } return (-ENOBUFS); } /* * M_ERROR Handling * ------------------------------------------------------------------------- * If some process drops an M_ERROR on the write queue, we will dicsonnect the * transport associsation, flush queues, disablw the queue and report the error * to management so that the transport stream can be unlinked and dealt with by * layer management. We must ensure that the rest of the M3UA knows that the * ASP is disabled. * * For now we sill just flush data awaiting acknowledgement. Later will will * recall data and redirect it to another ASP if possible. */ static int asp_w_error(queue_t * q, mblk_t * pdu) { lp_t *lp = (lp_t *) q->q_ptr; xp_t *xp = lp->xp; switch (xp->state) { case ASP_DOWN: case ASP_WACK_ASPUP: case ASP_WACK_ASPDN: case ASP_UNREG: case ASP_WRSP_REG: case ASP_WRSP_DEREG: case ASP_UP: } /* * Actually, we don't care what state we are in. */ if (xp->asur_count | xp->asac_count | xp->asia_count) { /* * We now have AS that are active for this ASP. We need to * move all of these AS to the AS-INACTIVE state. */ if (xp->asac_count) { } /* * We now have AS that are inactive for this ASP. We need to * move all of these AS to the AS-UNREG or AS-DOWN state. */ if (xp->asia_count) { } /* * We now have AS that are unregistered for this ASP. We need * to move all of these AS to the AS-DOWN state. */ if (xp->asur_count) { } } /* * FIXME: Just inform mangement and disable the queue here. */ if (!(lp->state & (NSF_IDLE | NSF_WACK_DREQ9))) { mblk_t *mp; N_discon_req_t *p; size_t mlen = sizeof(*p); if ((mp = ua_allocb(q, mlen, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->PRIM_type = N_DISCON_REQ; p->DISCON_reason = 0; /* FIXME */ p->RES_length = 0; p->RES_offset = 0; p->SEQ_number = 0; lp->state = NSF_WACK_DREQ9; qreply(q, mp); goto asp_w_error_lm_indicate; return (0); } return (-ENOBUFS); } asp_w_error_lm_indicate: return (0); /* ignore if we are already disconnecting */ } /* * M_HANGUP Handling * ------------------------------------------------------------------------- * If some process drops an M_HANGUP on the write queue, we will disconnect the * transport association, flush queues, diable the queue and report the error * to management so that the transport stream can be unlinked and dealt with by * layer management. We must ensure that the rest of the M3UA knows that the * ASP is disabled. * * For now we sill just flush data awaiting acknowledgement. Later will will * recall data and redirect it to another ASP if possible. */ static int asp_w_hangup(queue_t * q, mblk_t * mp) { } /* * M3UA --> SCTP (NPI) Transport Primitives * ------------------------------------------------------------------------- */ static int asp_w_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: return asp_w_data(q, mp); case M_CTL: return asp_w_ctl(q, mp); case M_ERROR: return asp_w_error(q, mp); case M_HANGUP: return asp_w_hangup(q, mp); case M_FLUSH: return ua_w_flush(q, mp); } return (QR_PASSFLOW); /* pass along */ } /* * ========================================================================= * * M3UA Driver Definitions * * ========================================================================= */ struct drv m3ua_driver = { M3UA_CMAJOR, M3UA_NMAJOR, M3UA_NMINOR, "m3ua", sizeof(mtp_addr_t), sizeof(mtp_addr_t), {&lm_r_prim, &lm_w_prim} , /* u_ops_lm */ {&mtpu_r_prim, &mtpu_w_prim} , /* u_ops_ss7 */ {&mtpp_r_prim, &mtpp_w_prim} , /* l_ops_ss7 */ {&sgp_r_prim, &sgp_w_prim} , /* l_ops_asp */ {&asp_r_prim, &asp_w_prim} , /* l_ops_sgp */ {&spp_r_prim, &spp_w_prim} , /* l_ops_spp */ {NULL,} };
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |