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/sua_n.c#ident "@(#) $RCSfile: sua_n.c,v $ $Name: $($Revision: 0.8.2.3 $) $Date: 2003/04/14 12:13:19 $" static char const ident[] = "$RCSfile: sua_n.c,v $ $Name: $($Revision: 0.8.2.3 $) $Date: 2003/04/14 12:13:19 $"; #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_mtp.h> #include "../lock.h" #include "../debug.h" #include "../bufq.h" #include "sua_n.h" #define SUA_DESCRIP "SUA STREAMS MULTIPLEXING DRIVER -- NPI INTERFACE." "\n" \ "Part of the OpenSS7 Stack for LiS STREAMS." #define SUA_COPYRIGHT "Copyright (c) 1997-2002 OpenSS7 Corp. All Rights Reserved" #define SUA_DEVICE "Supports OpenSS7 MTP Level 3 Drivers." #define SUA_CONTACT "Brian Bidulock <bidulock@openss7.org>" #define SUA_LICENSE "GPL" #define SUA_BANNER SUA_DESCRIP "\n" \ SUA_COPYRIGHT "\n" \ SUA_DEVICE "\n" \ SUA_CONTACT "\n" MODULE_AUTHOR(SUA_CONTACT); MODULE_DESCRIPTION(SUA_DESCRIP); MODULE_SUPPORTED_DEVICE(SUA_DEVICE); #ifdef MODULE_LICENSE MODULE_LICENSE(SUA_LICENSE); #endif /* * ========================================================================= * * NPI Provider (SUA) -> NPI User Primitives * * ========================================================================= */ /* * N_CONN_IND 11 - Incoming connection indication * ------------------------------------------------------------- */ STATIC INLINE int n_conn_ind(queue_t * q, mblk_t * cp) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_conn_ind_t *p; N_qos_sel_conn_sccp_t *qos; sua_addr_t *dst, *src; struct sua_cr *m = (struct sua_cr *) cp->b_rptr; size_t dst_len = sizeof(priv->dst) + priv->dst.alen; size_t src_len = sizeof(priv->src) + priv->src.alen; size_t qos_len = sizeof(*qos); size_t msg_len = sizeof(*p) + dst_len + src_len + qos_len; if ((1 << priv->i_state) & ~(NSF_IDLE | NSF_WRES_CIND)) goto efault; if (bufq_length(&priv->conq) >= priv->conind) goto erestart; if (!canputnext(priv->rq)) goto ebusy; if (!(mp = sua_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_CONN_IND; p->DEST_length = dst_len; p->DEST_offset = dst_len ? sizeof(*p) : 0; p->SRC_length = src_len; p->SRC_offset = src_len ? sizeof(*p) + dst_len : 0; p->SEQ_number = (ulong) cp; p->CONN_flags = REC_CONF_OPT | EX_DATA_OPT; p->QOS_length = qos_len; p->QOS_offset = qos_len ? sizeof(*p) + dst_len + src_len : 0; if (dst_len) { (void) m; /* FIXME */ dst = (typeof(dst)) mp->b_wptr; bcopy(dst, &priv->dst, dst_len); mp->b_wptr += dst_len; } if (src_len) { src = (typeof(src)) mp->b_wptr; bcopy(src, &priv->src, src_len); mp->b_wptr += src_len; } if (qos_len) { qos = ((typeof(qos)) mp->b_wptr)++; qos->n_qos_type = N_QOS_SEL_CONN_SCCP; qos->protocol_class = priv->pclass; } bufq_queue(&priv->conq, cp); priv->i_state = NS_WRES_CIND; putnext(priv->rq, mp); return (0); enobufs: err = -ENOBUFS; ptrace(("ERROR: No buffers\n")); goto error; ebusy: err = -EBUSY; ptrace(("ERROR: Flow controlled\n")); goto error; erestart: err = -ERESTART; ptrace(("ERROR: Too many connect inds\n")); goto error; efault: err = -EFAULT; ptrace(("SWERR: Indication in wrong state\n")); goto error; error: return (err); } /* * N_CONN_CON 12 - Connection established * --------------------------------------------------------- */ STATIC INLINE int n_conn_con(queue_t * q, uint pcls, uint flags, sua_addr_t * res, mblk_t * dp) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_conn_con_t *p; N_qos_sel_conn_sccp_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 ((priv->i_state != NS_WCON_CREQ)) goto efault; if (!canputnext(priv->rq)) goto ebusy; if (!(mp = sua_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_SCCP; qos->protocol_class = pcls; } mp->b_cont = dp; priv->i_state = NS_DATA_XFER; putnext(priv->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 SUA 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 n_discon_ind(queue_t * q, ulong orig, long reason, sua_addr_t * res, mblk_t * seq, mblk_t * dp) { sua_t *priv = SUA_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 << priv->i_state) & ~(NSF_WCON_CREQ | NSF_WRES_CIND | NSF_DATA_XFER | NSF_WCON_RREQ | NSF_WRES_RIND)) goto efault; if (!canputnext(priv->rq)) goto ebusy; if (!(mp = sua_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(&priv->conq, seq); freemsg(seq); } if (!bufq_length(&priv->conq)) priv->i_state = NS_IDLE; else priv->i_state = NS_WRES_CIND; mp->b_cont = dp; putnext(priv->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 n_data_ind(queue_t * q, uint more, mblk_t * dp) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_data_ind_t *p; N_qos_sel_data_sccp_t *qos; const size_t qos_len = sizeof(*qos); const size_t msg_len = sizeof(*p) + qos_len; if ((1 << priv->i_state) & ~(NSF_DATA_XFER)) goto efault; if (!canputnext(priv->rq)) goto ebusy; if (!(mp = sua_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_SCCP; qos->protocol_class = priv->pclass; /* FIXME */ qos->option_flags = priv->options; /* FIXME */ qos->importance = priv->imp; /* FIXME */ qos->sequence_selection = priv->sls; /* FIXME */ qos->message_priority = priv->mp; /* FIXME */ mp->b_cont = dp; putnext(priv->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 n_exdata_ind(queue_t * q, uint more, mblk_t * dp) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_exdata_ind_t *p; N_qos_sel_data_sccp_t *qos; const size_t qos_len = sizeof(*qos); const size_t msg_len = sizeof(*p) + qos_len; if ((1 < priv->i_state) & ~(NSF_DATA_XFER)) goto efault; if (!canputnext(priv->rq)) goto ebusy; if (!(mp = sua_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_SCCP; qos->protocol_class = priv->pclass; /* FIXME */ qos->option_flags = priv->options; /* FIXME */ qos->importance = priv->imp; /* FIXME */ qos->sequence_selection = priv->sls; /* FIXME */ qos->message_priority = priv->mp; /* FIXME */ mp->b_cont = dp; putnext(priv->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 n_info_ack(queue_t * q) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_info_ack_t *p; N_qos_sel_info_sccp_t *qos; N_qos_range_info_sccp_t *qor; sua_addr_t *add, *ss = &priv->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 = sua_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(sua_addr_t) + SUA_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 | ((priv->flags & SUA_FLAG_DEFAULT_RC_SEL) ? DEFAULT_RC_SEL : 0); p->NIDU_size = -1; p->SERV_type = N_CONS | N_CLNS; p->CURRENT_state = priv->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_SCCP; qos->protocol_class = priv->pclass; qos->option_flags = priv->options; } if (qor_len) { qor = ((typeof(qor)) mp->b_wptr)++; qor->n_qos_type = N_QOS_RANGE_INFO_SCCP; qor->protocol_classes = priv->pclass; qor->sequence_selection = priv->sls; } if (pro_len) { pro = ((typeof(pro)) mp->b_wptr)++; *pro = 3; } putnext(priv->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 * --------------------------------------------------------- */ STATIC INLINE int n_bind_ack(queue_t * q) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_bind_ack_t *p; sua_addr_t *add, *ss = &priv->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 = sua_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 = priv->conind; p->TOKEN_value = (ulong) priv->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; } priv->i_state = NS_IDLE; putnext(priv->rq, mp); return (0); enobufs: err = -ENOBUFS; ptrace(("ERROR: No buffers\n")); goto error; error: return (err); } /* * N_ERROR_ACK 18 - Error Acknowledgement * --------------------------------------------------------- */ STATIC int n_error_ack(queue_t * q, int prim, int err) { sua_t *priv = SUA_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 = sua_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 (priv->i_state) { case NS_WACK_OPTREQ: case NS_WACK_UREQ: case NS_WCON_CREQ: priv->i_state = NS_IDLE; break; case NS_WCON_RREQ: priv->i_state = NS_DATA_XFER; break; case NS_WACK_BREQ: priv->i_state = NS_UNBND; break; case NS_WACK_CRES: priv->i_state = NS_WRES_CIND; break; case NS_WACK_DREQ6: priv->i_state = NS_WCON_CREQ; break; case NS_WACK_DREQ7: priv->i_state = NS_WRES_CIND; break; case NS_WACK_DREQ9: priv->i_state = NS_DATA_XFER; break; case NS_WACK_DREQ10: priv->i_state = NS_WCON_RREQ; break; case NS_WACK_DREQ11: priv->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(priv->rq, mp); return (0); enobufs: err = -ENOBUFS; ptrace(("ERROR: No buffers\n")); goto error; error: return (err); } /* * N_OK_ACK 19 - Success Acknowledgement * --------------------------------------------------------- */ STATIC int n_ok_ack(queue_t * q, ulong prim, ulong seq, ulong tok) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_ok_ack_t *p; const size_t msg_len = sizeof(*p); if (!(mp = sua_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 (priv->i_state) { case NS_WACK_OPTREQ: priv->i_state = NS_IDLE; break; case NS_WACK_RRES: priv->i_state = NS_DATA_XFER; break; case NS_WACK_UREQ: priv->i_state = NS_UNBND; break; case NS_WACK_CRES: { queue_t *aq = (queue_t *) tok; sua_t *ap = SUA_PRIV(aq); if (ap) { ap->i_state = NS_DATA_XFER; sua_cleanup_read(q); /* deliver to user what is possible */ sua_transmit_wakeup(q); /* reply to peer what is necessary */ } if (seq) { bufq_unlink(&priv->conq, (mblk_t *) seq); freemsg((mblk_t *) seq); } if (aq != priv->rq) { if (bufq_length(&priv->conq)) priv->i_state = NS_WRES_CIND; else priv->i_state = NS_IDLE; } break; } case NS_WACK_DREQ7: if (seq) bufq_unlink(&priv->conq, (mblk_t *) seq); case NS_WACK_DREQ6: case NS_WACK_DREQ9: case NS_WACK_DREQ10: case NS_WACK_DREQ11: if (bufq_length(&priv->conq)) priv->i_state = NS_WRES_CIND; else priv->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(priv->rq, mp); return (0); enobufs: err = -ENOBUFS; ptrace(("ERROR: No buffers\n")); goto error; error: return (err); } /* * N_UNITDATA_IND 20 - Connection-less daa receive indication * --------------------------------------------------------------- */ STATIC INLINE int n_unitdata_ind(queue_t * q, sua_addr_t * src, sua_addr_t * dst, mblk_t * dp) { sua_t *priv = SUA_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 << priv->i_state) & ~(NSF_IDLE)) goto efault; if (!canputnext(priv->rq)) goto ebusy; if (!(mp = sua_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(priv->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 n_uderror_ind(queue_t * q, uint error, sua_addr_t * dst, mblk_t * dp) { sua_t *priv = SUA_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 << priv->i_state) & ~(NSF_IDLE)) goto efault; if (!canputnext(priv->rq)) goto ebusy; if (!(mp = sua_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(priv->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 n_datack_ind(queue_t * q) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_datack_ind_t *p; N_qos_sel_data_sccp_t *qos; const size_t qos_len = sizeof(*qos); const size_t msg_len = sizeof(*p) + qos_len; if ((1 << priv->i_state) & ~(NSF_DATA_XFER)) goto efault; if (!canputnext(priv->rq)) goto ebusy; if (!(mp = sua_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_SCCP; qos->protocol_class = priv->pclass; /* FIXME */ qos->option_flags = priv->options; /* FIXME */ qos->importance = priv->imp; /* FIXME */ qos->sequence_selection = priv->sls; /* FIXME */ qos->message_priority = priv->mp; /* FIXME */ putnext(priv->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 n_reset_ind(queue_t * q, ulong orig, ulong reason, mblk_t * cp) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_reset_ind_t *p; const size_t msg_len = sizeof(*p); if ((1 << priv->i_state) & ~(NSF_DATA_XFER)) goto efault; if (!canputnext(priv->rq)) goto ebusy; if (!(mp = sua_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(&priv->conq, cp); priv->i_state = NS_WRES_RIND; putnext(priv->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 n_reset_con(queue_t * q) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_reset_con_t *p; const size_t msg_len = sizeof(*p); if ((1 << priv->i_state) & ~(NSF_WCON_RREQ)) goto efault; if (!canputnext(priv->rq)) goto ebusy; if (!(mp = sua_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; priv->i_state = NS_DATA_XFER; putnext(priv->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 n_recover_ind(queue_t * q) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_recover_ind_t *p; const size_t msg_len = sizeof(*p); if (!canputnext(priv->q)) goto ebusy; if (!(mp = sua_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; priv->i_state = NS_DATA_XFER; putnext(priv->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 n_retrieve_ind(queue_t * q, mblk_t * dp) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_retrieve_ind_t *p; const size_t msg_len = sizeof(*p); if ((1 << priv->i_state) & ~(NSF_IDLE)) goto efault; if (!canputnext(priv->q)) goto ebusy; if (!(mp = sua_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; priv->i_state = NS_IDLE; putnext(priv->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 n_retrieve_con(queue_t * q) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *mp; N_retrieve_con_t *p; const size_t msg_len = sizeof(*p); if ((1 << priv->i_state) & ~(NSF_IDLE)) goto efault; if (!canputnext(priv->q)) goto ebusy; if ((mp = sua_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; priv->i_state = NS_IDLE; putnext(priv->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 /* * ========================================================================= * * STATE MACHINE Interface * * ========================================================================= */ STATIC int sua_conn_ind(sua_t * priv, mblk_t * cp) { return n_conn_ind(priv->rq, cp); } STATIC int sua_conn_con(sua_t * priv, uint pcls, uint flags, sua_addr_t * res, mblk_t * dp) { return n_conn_con(priv->rq, pcls, flags, res, dp); } STATIC int sua_data_ind(sua_t * priv, uint exp, uint more, mblk_t * dp) { if (exp) return n_exdata_ind(priv->rq, more, dp); else return n_data_ind(priv->rq, more, dp); } STATIC int sua_datack_ind(sua_t * priv) { return n_datack_ind(priv->rq); } STATIC int sua_reset_ind(sua_t * priv, ulong orig, ulong reason, mblk_t * cp) { return n_reset_ind(priv->rq, orig, reason, cp); } STATIC int sua_reset_con(sua_t * priv) { return n_reset_con(priv->rq); } STATIC int sua_discon_ind(sua_t * priv, ulong orig, long reason, sua_addr_t * res, mblk_t * cp, mblk_t * dp) { return n_discon_ind(priv->rq, orig, reason, res, cp, dp); } STATIC int sua_ordrel_ind(sua_t * priv) { return sua_ordrel_req(priv->rq); } STATIC int sua_unitdata_ind(sua_t * priv, sua_addr_t * dst, sua_addr_t * src, uint pcl, uint opt, uint imp, uint seq, uint pri, mblk_t * dp) { return n_unitdata_ind(priv->rq, src, dst, dp); } STATIC int sua_uderror_ind(sua_t * priv, uint cause, sua_addr_t * dst, sua_addr_t * src, uint pcl, uint opt, uint imp, uint seq, uint pri, mblk_t * dp) { (void) src; return n_uderror_ind(priv->rq, cause, dst, dp); } STATIC struct sua_ifops sua_n_ops = { sua_conn_ind, sua_conn_con, sua_data_ind, sua_datack_ind, sua_reset_ind, sua_reset_con, sua_discon_ind, sua_ordrel_ind, sua_unitdata_ind, sua_uderror_ind }; /* * ========================================================================= * * NPI User --> NPI Provider (SUA) Primitives * * ========================================================================= */ /* * 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 n_conn_req(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_PRIV(q); sua_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_sccp_t *qos = (typeof(qos)) (mp->b_rptr + p->QOS_offset); if (priv->i_state != NS_IDLE) goto noutstate priv->i_state = NS_WCON_CREQ; if (mlen < sizeof(*p)) goto einval; if (p->QOS_length && mlen < p->QOS_offset + p->QOS_length) goto nbadopt3; if (p->QOS_length && qos->n_qos_type != N_QOS_SEL_CONN_SCCP) goto nbadqostype; if (p->QOS_length && qos->protocol_class > 3) goto nbadopt2; if (p->QOS_length && qos->protocol_class != 2 && qos->protocol_class != 3) goto nnotsupport; if (p->QOS_length && p->QOS_length != sizeof(*qos)) goto nbadopt; a = (sua_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(sua_addr_t) + a->alen) || a->ssn) goto nbadaddr; if (priv->cred.cr_uid == 0 || a->ssn) goto naccess; fixme(("select a source local reference number\n")); if (p->QOS_length) priv->pclass = qos->protocol_class; priv->flags &= ~(SUA_FLAG_REC_CONF_OPT | SUA_FLAG_EX_DATA_OPT); if (p->CONN_flags & REC_CONF_OPT) priv->flags |= SUA_FLAG_REC_CONF_OPT; if (p->CONN_flags & EX_DATA_OPT) priv->flags |= SUA_FLAG_EX_DATA_OPT; if ((err = sua_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; nnotsupport: err = NNOTSUPPORT; ptrace(("ERROR: protocol class not supported\n")); goto error; nbadopt2: err = NBADOPT; ptrace(("ERROR: invalid protocol class\n")); goto error; nbadqostype: err = NBADQOSTYPE; ptrace(("ERROR: QOS structure type not supported\n")); goto error; nbadopt3: 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 n_error_ack(q, N_CONN_REQ, err); } /* * N_CONN_RES 1 - Accept previous connection indication * --------------------------------------------------------------- * IMPLEMENTATION NOTES:- Sequence numbers are actually the address of the * mblk which contains the CR message and contains the contents of this * message as a connection indication. To find if a particular sequence * number is valid, we walk the connection indication queue looking for a * mblk with the same address as the sequence number. Sequence numbers are * only valid on the stream for which the connection indication is queued. */ STATIC mblk_t *n_seq_check(sua_t * priv, ulong seq) { mblk_t *mp; for (mp = bufq_head(&priv->conq); mp && mp != (mblk_t *) seq; mp = mp->b_next); usual(mp); return (mp); } STATIC sua_t *n_tok_check(sua_t * priv, ulong tok) { priv_t *ap; queue_t *aq = (queue_t *) tok; for (ap = sua_opens; ap && ap->rq != aq; ap = ap->next); usual(ap); return ((sua_t *) ap); } STATIC int n_conn_res(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_PRIV(q); int err; mblk_t *cp; sua_t *ap; size_t mlen = mp->b_wptr - mp->b_rptr; N_conn_res_t *p = (typeof(p)) mp->b_rptr; N_qos_sel_conn_sccp_t *qos = (typeof(qos)) (mp->b_rptr + p->QOS_offset); uint ap_oldstate; uint ap_oldflags; if (priv->i_state != NS_WRES_CIND) goto noutstate; priv->i_state = NS_WACK_CRES; 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_SCCP) goto nbadqostype; if (p->QOS_length && p->QOS_length != sizeof(*qos)) goto nbadopt; if (!(cp = n_seq_check(priv, p->SEQ_number))) goto nbadseq; if (!(ap = n_tok_check(priv, p->TOKEN_value)) || (ap != priv && ((1 << ap->i_state) & ~(NSF_UNBND | NSF_IDLE)))) goto nbadtoken2; if (ap->i_state != NS_IDLE || ap->conind) goto nbadtoken; /* * protect at least r00t streams from users */ if (priv->cred.cr_uid != 0 && ap->cred.cr_uid == 0) goto naccess; ap_oldstate = ap->i_state; ap_oldflags = ap->flags; ap->i_state = NS_DATA_XFER; ap->flags &= ~(SUA_FLAG_REC_CONF_OPT | SUA_FLAG_EX_DATA_OPT); if (p->CONN_flags & REC_CONF_OPT) ap->flags |= SUA_FLAG_REC_CONF_OPT; if (p->CONN_flags & EX_DATA_OPT) ap->flags |= SUA_FLAG_EX_DATA_OPT; if ((err = sua_conn_res(q, cp, ap, mp->b_cont))) goto badconn; mp->b_cont = NULL; /* absorbed mp->b_cont */ return n_ok_ack(q, N_CONN_RES, p->SEQ_number, p->TOKEN_value); badconn: ap->i_state = ap_oldstate; ap->flags = ap_oldflags; err = err; ptrace(("ERROR: couldn't connect\n")); goto error; naccess: err = NACCESS; ptrace(("ERROR: no access to accepting queue\n")); goto error; nbadtoken: err = NBADTOKEN; ptrace(("ERROR: accepting queue is listening\n")); goto error; nbadtoken2: err = NBADTOKEN; ptrace(("ERROR: accepting queue id is invalid\n")); goto error; nbadseq: err = NBADSEQ; ptrace(("ERROR: connection ind reference is invalid\n")); goto error; nbadopt: err = NBADOPT; ptrace(("ERROR: quality of service options are bad\n")); goto error; nbadqostype: err = NBADQOSTYPE; ptrace(("ERROR: quality of service options are bad\n")); goto error; nbadopt2: err = NBADOPT; ptrace(("ERROR: quality of service options are bad\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 n_error_ack(q, N_CONN_RES, err); } /* * N_DISCON_REQ 2 - NC disconnection request * --------------------------------------------------------- */ STATIC int n_discon_req(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_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 (priv->i_state) { case NS_WCON_CREQ: priv->i_state = NS_WACK_DREQ6; break; case NS_WRES_CIND: priv->i_state = NS_WACK_DREQ7; break; case NS_DATA_XFER: priv->i_state = NS_WACK_DREQ9; break; case NS_WCON_RREQ: priv->i_state = NS_WACK_DREQ10; break; case NS_WRES_RIND: priv->i_state = NS_WACK_DREQ11; break; default: goto noutstate; } if (mlen < sizeof(*p)) goto einval; if (p->RES_length) goto nbadaddr; if (priv->i_state == NS_WACK_DREQ7 && !(cp = n_seq_check(priv, p->SEQ_number))) goto nbadseq; /* * XXX: What to do with the disconnect reason? Nothing? */ if ((err = sua_discon_req(q, cp))) goto baddisconn; return n_ok_ack(q, N_DISCON_REQ, p->SEQ_number, 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 n_error_ack(q, N_DISCON_REQ, err); } /* * N_DATA_REQ 3 - Connection-Mode data transfer request * -------------------------------------------------------------- */ STATIC int n_error_reply(queue_t * q, int err) { sua_t *priv = SUA_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 = sua_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(priv->rq, mp); return (0); } return (-ENOBUFS); } STATIC INLINE int n_write(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_PRIV(q); int err; if (priv->i_state == NS_IDLE) goto discard; if ((1 << priv->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND)) goto eproto; if ((err = sua_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 n_error_reply(q, err); } STATIC INLINE int n_data_req(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_PRIV(q); int err; N_qos_sel_data_sccp_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 (priv->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_SCCP) qos = NULL; } if ((1 << priv->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND)) goto eproto; exp = 0; /* FIXME */ more = 0; /* FIXME */ rcpt = 0; /* FIXME */ if ((err = sua_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 n_error_reply(q, err); } /* * N_EXDATA_REQ 4 - Expedited data request * --------------------------------------------------------- */ STATIC INLINE int n_exdata_req(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_PRIV(q); int err; N_qos_sel_data_sccp_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 (priv->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_SCCP) qos = NULL; } if ((1 << priv->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND)) goto eproto; exp = 0; /* FIXME */ more = 0; /* FIXME */ rcpt = 0; /* FIXME */ if ((err = sua_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 n_error_reply(q, err); } /* * N_INFO_REQ 5 - Information request * --------------------------------------------------------- */ STATIC int n_info_req(queue_t * q, mblk_t * mp) { (void) mp; return n_info_ack(q); } /* * N_BIND_REQ 6 - Bind a NS user to network address * ---------------------------------------------------------- */ STATIC int n_bind_req(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_PRIV(q); int err; size_t mlen = mp->b_wptr - mp->b_rptr; N_bind_req_t *p = (typeof(p)) mp->b_rptr; sua_addr_t *a; if (priv->i_state != NS_UNBND) goto noutstate; priv->i_state = NS_WACK_BREQ; if (mlen < sizeof(*p)) goto einval; a = (sua_addr_t *) (mp->b_rptr + p->ADDR_offset); if ((mlen < p->ADDR_offset + p->ADDR_length) || (p->ADDR_length < sizeof(sua_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 (priv->cred.cr_uid != 0 && !a->ssn) goto naccess; if ((err = sua_bind_req(q, a, p->CONIND_number))) goto error; return n_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 n_error_ack(q, N_BIND_REQ, err); } /* * N_UNBIND_REQ 7 - Unbind NS user from network address * ------------------------------------------------------------ */ STATIC int n_unbind_req(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_PRIV(q); int err; N_unbind_req_t *p = (typeof(p)) mp->b_rptr; (void) p; if (priv->i_state != NS_IDLE) goto noutstate; priv->i_state = NS_WACK_UREQ; if ((err = sua_unbind_req(q))) goto error; return n_ok_ack(q, N_UNBIND_REQ, 0, 0); noutstate: err = NOUTSTATE; ptrace(("ERROR: would place interface out of state\n")); goto error; error: return n_error_ack(q, N_UNBIND_REQ, err); } /* * N_UNITDATA_REQ 8 - Unitdata request * --------------------------------------------------------- */ STATIC int n_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 n_unitdata_req(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_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_sccp_t *qos; sua_addr_t *dst; ulong pclass, options, import, seq, pri; if (dlen == 0 || dlen > priv->mtu) goto eproto; if (priv->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 (priv->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_SCCP) qos = NULL; } pclass = qos ? qos->protocol_class : priv->pclass; options = qos ? qos->option_flags : priv->options; import = qos ? qos->importance : priv->imp; seq = qos ? qos->sequence_selection : priv->sls; pri = qos ? qos->message_priority : priv->mp; if ((err = sua_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 n_uderror_reply(q, mp, err); } /* * N_OPTMGMT_REQ 9 - Options management request * --------------------------------------------------------- */ STATIC INLINE int n_optmgmt_req(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_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_sccp_t *qos = (typeof(qos)) (mp->b_rptr + p->QOS_offset); if (priv->i_state == NS_IDLE) priv->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_SCCP) goto nbadqostype; if (p->QOS_length && p->QOS_length != sizeof(*qos)) goto nbadopt2; if (p->QOS_length && qos->protocol_class > 3) goto nbadopt3; if (p->QOS_length) { if (qos->protocol_class != -1UL) priv->pclass = qos->protocol_class; if (qos->option_flags != -1UL) priv->options = qos->option_flags; } if (p->OPTMGMT_flags & DEFAULT_RC_SEL) priv->flags |= SUA_FLAG_DEFAULT_RC_SEL; else priv->flags &= ~SUA_FLAG_DEFAULT_RC_SEL; return n_ok_ack(q, N_OPTMGMT_REQ, 0, 0); nbadopt3: err = NBADOPT; ptrace(("ERROR: bad protocol class\n")); goto error; 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 n_error_ack(q, N_OPTMGMT_REQ, err); } /* * N_DATACK_REQ 23 - Data acknowledgement request * --------------------------------------------------------- */ STATIC INLINE int n_datack_req(queue_t * q, mblk_t * mp) { (void) mp; fixme(("SWERR: Write this function\n")); return n_error_ack(q, N_DATACK_REQ, NNOTSUPPORT); } /* * N_RESET_REQ 25 - NC reset request * --------------------------------------------------------- */ STATIC int n_reset_req(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_PRIV(q); int err; size_t mlen = mp->b_wptr - mp->b_rptr; N_reset_req_t *p = (typeof(p)) mp->b_rptr; if (priv->i_state == NS_IDLE) goto discard; if (priv->i_state != NS_DATA_XFER) goto noutstate; priv->i_state = NS_WCON_RREQ; (void) p; (void) mlen; if ((err = sua_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 n_error_ack(q, N_RESET_REQ, err); } /* * N_RESET_RES 27 - Reset processing accepted * --------------------------------------------------------- */ STATIC INLINE int n_reset_res(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_PRIV(q); int err; size_t mlen = mp->b_wptr - mp->b_rptr; N_reset_res_t *p = (typeof(p)) mp->b_rptr; if (priv->i_state == NS_IDLE) goto discard; if (priv->i_state != NS_WRES_RIND) goto noutstate; priv->i_state = NS_WACK_RRES; (void) p; (void) mlen; if ((err = sua_reset_res(q))) goto error; return n_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 n_error_ack(q, N_RESET_RES, err); } STATIC int n_other_req(queue_t * q, mblk_t * mp) { ulong prim = *((ulong *) mp->b_rptr); return n_error_ack(q, prim, NNOTSUPPORT); } /* * ========================================================================= * * STREAMS Message Handling * * ========================================================================= * * M_IOCTL Handling * * ------------------------------------------------------------------------- */ STATIC int sua_n_w_ioctl(queue_t * q, mblk_t * mp) { int ret = -EINVAL; // void *arg = mp->b_cont?mp->b_cont->b_rptr:NULL; struct iocblk *iocp = (typeof(iocp)) mp->b_wptr; int cmd = iocp->ioc_cmd; switch (_IOC_TYPE(cmd) >> 8) { case __SID: ret = -EOPNOTSUPP; break; case SUA_IOC_MAGIC: if (iocp->ioc_count >= _IOC_SIZE(cmd)) { int nr = _IOC_NR(cmd); switch (nr) { default: ret = -EOPNOTSUPP; break; } } break; default: ret = -EOPNOTSUPP; break; } mp->b_datap->db_type = ret < 0 ? M_IOCNAK : M_IOCACK; iocp->ioc_error = ret < 0 ? -ret : 0; iocp->ioc_rval = ret < 0 ? -1 : ret; qreply(q, mp); return (0); } /* * ------------------------------------------------------------------------- * * M_PROTO, M_PCPROTO Handling * * ------------------------------------------------------------------------- */ STATIC int sua_n_w_proto(queue_t * q, mblk_t * mp) { int rtn; ulong prim; sua_t *priv = SUA_PRIV(q); ulong oldstate = priv->i_state; switch ((prim = *((ulong *) mp->b_rptr))) { case N_CONN_REQ: rtn = n_conn_req(q, mp); break; case N_CONN_RES: rtn = n_conn_res(q, mp); break; case N_DISCON_REQ: rtn = n_discon_req(q, mp); break; case N_DATA_REQ: rtn = n_data_req(q, mp); break; case N_EXDATA_REQ: rtn = n_exdata_req(q, mp); break; case N_INFO_REQ: rtn = n_info_req(q, mp); break; case N_BIND_REQ: rtn = n_bind_req(q, mp); break; case N_UNBIND_REQ: rtn = n_unbind_req(q, mp); break; case N_OPTMGMT_REQ: rtn = n_optmgmt_req(q, mp); break; case N_RESET_REQ: rtn = n_reset_req(q, mp); break; case N_RESET_RES: rtn = n_reset_res(q, mp); break; case N_UNITDATA_REQ: rtn = n_unitdata_req(q, mp); break; default: rtn = n_other_req(q, mp); break; } if (rtn < 0) { seldom(); priv->i_state = oldstate; } return (rtn); } /* * ------------------------------------------------------------------------- * * M_DATA Handling * * ------------------------------------------------------------------------- */ STATIC int sua_n_w_data(queue_t * q, mblk_t * mp) { (void) mp; return n_write(q, mp); } /* * ------------------------------------------------------------------------- * * Other messages * * ------------------------------------------------------------------------- */ STATIC int sua_n_w_other(queue_t * q, mblk_t * mp) { sua_t *priv = SUA_PRIV(q); cmn_err(CE_WARN, "Unsupported lock type %d on WR(q) %d\n", mp->b_datap->db_type, getminor(priv->devid)); return (-EOPNOTSUPP); } /* * ========================================================================= * * QUEUE PUT and SERVICE routines * * ========================================================================= * * WRITE PUT and SERVICE (Message from above SUA-User --> SUA-Provider) * ------------------------------------------------------------------------- */ STATIC int sua_n_w_prim(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: return sua_n_w_data(q, mp); case M_PROTO: case M_PCPROTO: return sua_n_w_proto(q, mp); case M_FLUSH: return sua_w_flush(q, mp); case M_IOCTL: return sua_n_w_ioctl(q, mp); default: return sua_n_w_other(q, mp); } return (-EOPNOTSUPP); } /* * ========================================================================= * * OPEN and CLOSE * * ========================================================================= * * OPEN * ------------------------------------------------------------------------- */ int sua_n_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp) { sua_t *priv; priv_t *priv, **privp; int cmajor = getmajor(*devp); int cminor = getminor(*devp); if (sflag == MODOPEN || WR(q)->q_next) { ptrace(("ERROR: Can't push as module\n")); return (EIO); } if (SUA_PRIV(q) != NULL) { ptrace(("INFO: Device already open\n")); return (0); } if (sflag == CLONEOPEN) { ptrace(("INFO: Clone open in effect\n")); cmajor = SUA_CMAJOR; cminor = 0; } if (cmajor == SUA_CMAJOR && cminor == 0 && sflag != CLONEOPEN) { ptrace(("INFO: Clone minor opened\n")); sflag = CLONEOPEN; cminor = 1; } for (privp = &sua_opens; *privp; privp = &(*privp)->next) { ushort dmajor = getmajor((*privp)->u.devid); if (cmajor < dmajor) break; if (cmajor == dmajor) { ushort dminor = getminor((*privp)->u.devid); if (cminor < dminor) break; if (cminor == dminor) { if (sflag == CLONEOPEN) { if (++cminor >= SUA_NMINOR) { if (++cmajor >= SUA_CMAJOR + SUA_NMAJOR) break; cminor = 0; } continue; } ptrace(("ERROR: Requested device (%d,%d) in use\n", cmajor, cminor)); return (EIO); } } } if (cmajor >= SUA_CMAJOR + SUA_NMINOR) { ptrace(("ERROR: No devices available\n")); return (ENXIO); } if (!((void *) priv = (void *) priv = kmalloc(sizeof(*priv), GFP_KERNEL))) { ptrace(("ERROR: Could not allocate private structure\n")); return (ENOMEM); } *devp = makedevice(cmajor, cminor); ptrace(("INFO: Making device major %d, minor %d\n", cmajor, cminor)); bzero(priv, sizeof(*priv)); SUA_PRIV(q) = SUA_PRIV(WR(q)) = priv; priv->rq = q; priv->wq = WR(q); priv->devid = *devp; priv->ops.w_prim = &sua_n_w_prim; priv->ops.r_prim = &sua_u_r_prim; priv->ifops = &sua_n_ops; if ((priv->next = *privp)) priv->next->prev = &priv->next; priv->prev = privp; *privp = priv; return (0); } /* * CLOSE * ------------------------------------------------------------------------- */ int sua_n_close(queue_t * q, int flag, cred_t * crp) { sua_t *priv = SUA_PRIV(q); if (!priv) return (EIO); if (priv->mtp) { /* * Detach this SUA stream from whatever SP streams it is * using on the lower half. */ mtp_t *mtp = priv->mtp; if ((--priv->mtp->use_count) <= 0) { #if 0 /* doesn't really apply to mtp's */ /* * Unbind the SP stream if it is not being used by * any SUA streams. */ mblk_t *mp; N_unbind_req_t *p; if (!(mp = allocb(sizeof(*p), BPRI_MED))) return (ENOSR); mp->b_datap->db_type = M_PCPROTO; p = (N_unbind_req_t *) mp->b_wptr; p->PRIM_type = N_UNBIND_REQ; mp->b_wptr += sizeof(*p); mtp->state = NS_WACK_UREQ; putnext(WR(mtp->q), mp); #endif mtp->use_count = 0; } } /* * TODO: Might want to do some more things to deallocate some SUA * structures associated with the module. I.e. some SUA bindings. */ if (priv->rbid) unbufcall(xchg(&priv->rbid, 0)); if (priv->wbid) unbufcall(xchg(&priv->wbid, 0)); if ((*(priv->prev) = priv->next)) priv->next->prev = priv->prev; SUA_PRIV(WR(q)) = SUA_PRIV(RD(q)) = NULL; kfree(priv); return (0); } /* * ========================================================================= * * Linux Kernel Module Initialization * * ========================================================================= */ int init_module(void) { cmn_err(CE_NOTE, SUA_BANNER); /* console splash */ return (0); } void cleanup_module(void) { return; }
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |