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/sscop/sscop_n.c#ident "@(#) $RCSfile: sscop_n.c,v $ $Name: $($Revision: 0.8.2.2 $) $Date: 2003/04/14 12:13:34 $" static char const ident[] = "$RCSfile: sscop_n.c,v $ $Name: $($Revision: 0.8.2.2 $) $Date: 2003/04/14 12:13:34 $"; #define SSCOP_DESCRIP "SSCOP/IP STREAMS DRIVER." #define SSCOP_COPYRIGHT "Copyright (c) 1997-2002 OpenSS7 Corporation. All Rights Reserved." #define SSCOP_DEVICE "Part of the OpenSS7 Stack for LiS STREAMS." #define SSCOP_CONTACT "Brian Bidulock <bidulock@openss7.org>" #define SSCOP_LICENSE "GPL" #define SSCOP_BANNER SSCOP_DESCRIP "\n" \ SSCOP_COPYRIGHT "\n" \ SSCOP_DEVICE "\n" \ SSCOP_CONTACT "\n" #ifdef MODULE MODULE_AUTHOR(SSCOP_CONTACT); MODULE_DESCRIPTION(SSCOP_DESCRIP); MODULE_SUPPORTED_DEVICE(SSCOP_DEVICE); #ifdef MODULE_LICENSE MODULE_LICENSE(SSCOP_LICENSE); #endif #define MODULE_STATIC static #else #define MOD_INC_USE_COUNT #define MOD_DEC_USE_COUNT #define MODULE_STATIC #endif #ifdef SSCOP_DEBUG static int sscop_debug = SSCOP_DEBUG; #else static int sscop_debug = 2; #endif #ifndef SSCOP_CMAJOR #define SSCOP_CMAJOR 240 #endif #define SSCOP_NMINOR 255 #ifdef LIS_2_12 #define INT int #else #define INT void #endif typedef void (*bufcall_fnc_t) (long); /* * ========================================================================= * * STREAMS Definitions * * ========================================================================= */ static struct module_info sscop_info = { 0, /* Module ID number *//* FIXME */ "sscop", /* Module name */ 1, /* Min packet size accepted */ 512, /* Max packet size accepted */ 8 * 512, /* Hi water mark */ 1 * 512 /* Lo water mark */ }; static INT sscop_rput(queue_t *, mblk_t *); static INT sscop_rsrv(queue_t *); static int sscop_open(queue_t *, dev_t *, int, int, cred_t *); static int sscop_close(queue_t *, int, cred_t *); static struct qinit sscop_rinit = { sscop_rput, /* Read put (msg from below) */ sscop_rsrv, /* Read queue service */ sscop_open, /* Each open */ sscop_close, /* Last close */ NULL, /* Admin (not used) */ &sscop_info, /* Information */ NULL /* Statistics */ }; static INT sscop_wput(queue_t *, mblk_t *); static INT sscop_wsrv(queue_t *); static struct qinit sscop_winit = { sscop_wput, /* Write put (msg from above) */ sscop_wsrv, /* Write queue service */ NULL, /* Each open */ NULL, /* Last close */ NULL, /* Admin (not used) */ &sscop_info, /* Information */ NULL /* Statistics */ }; MODULE_STATIC struct streamtab sscop_info = { &sscop_rinit, /* Upper read queue */ &sscop_winit, /* Upper write queue */ NULL, /* Lower read queue */ NULL /* Lower write queue */ }; /* * ========================================================================= * * SSCOP Private Structure * * ========================================================================= */ struct sscop { struct sscop *next; /* linkage for private structure list */ struct sscop *pprev; /* linkage for private structure list */ queue_t *wq; queue_t *rq; }; typedef struct sscop sscop_t; /* * ========================================================================= * * SSCOP Message Structures * * ========================================================================= */ /* * ========================================================================= * * SSCOP N-Provider --> N-User Primitives (M_CTL, M_PROTO, M_PCPROTO) * * ========================================================================= * * These functions package up the necessary primitive format given the * input parameters necessary to create them. They are all inlined and * return NULL if a message block could not be allocated. The reason for not * passing the mblk up the queue yet is because the caller may wish to * allocate the response before committing to processing the action which * causes these indications and confirmations to the N-User. */ #include "sscop_n_prov.h" /* * ========================================================================= * * SSCOP T-Provider --> N-Provider (IP) Primitives (M_CTL, M_PROTO, M_PCPROTO) * * ========================================================================= * * These functions package up the necessary primitive format given the * input parameters necessary to create them. They are all inlined and * return NULL if a message block could not be allocated. */ /* * N_INFO_REQ * ------------------------------------------------------------------------- */ static inline mblk_t *n_info_req(void) { mblk_t *mp; N_info_req_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_info_req_t *) mp->b_wptr; p->PRIM_type = N_INFO_REQ; mp->b_wptr += sizeof(*p); } return (mp); } /* * N_OPTMGMT_REQ * ------------------------------------------------------------------------- * There isn't much point in this for IP except to set the TOS bits in the IP * header and to indicate whether to set the DF bit to frag or not. Also, * default IP options could be set here. */ static inline mblk_t *n_optmgmt_req(caddr_t qos_ptr, size_t qos_len, uint flags) { mblk_t *mp; N_optmgmt_req_t *p; if ((mp = allocb(sizeof(*p) + qos_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_optmgmt_req_t *) mp->b_wptr; p->PRIM_type = N_OPTMGMT_REQ; p->QOS_length = qos_len; p->QOS_offset = qos_len ? sizeof(*p) : 0; p->OPTMGMT_flags = flags; mp->b_wptr += sizeof(*p); bcopy(qos_ptr, mp->b_wptr, qos_len); mp->b_wptr += qos_len; } return (mp); } /* * N_BIND_REQ * ------------------------------------------------------------------------- * For IP for SSCOP we use this to bind the stream to the Protocol ID which is * to be used for SSCOP (132). The network layer will then pass messages up * to the SSCOP. */ static inline mblk_t *n_bind_req(void) static inline mblk_t *n_bind_req(caddr_t add_ptr, size_t add_len, int cons, uint flags, caddr_t pro_ptr, size_t pro_len) { mblk_t *mp; N_bind_req_t *p; if ((mp = allocb(sizeof(*p) + add_len + pro_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_bind_req_t *) mp->b_wptr; p->PRIM_type = N_BIND_REQ; p->ADDR_length = 0; p->ADDR_offset = 0; p->CONIND_number = 0; p->BIND_flags = flags; p->PROTOID_length = pro_len; p->PROTOID_offset = pro_len ? sizeof(*p) + add_len : 0; /* might pad */ mp->b_wptr += sizeof(*p); bcopy(add_ptr, mp->b_wptr, add_len); mp->b_wptr += add_len; bcopy(pro_ptr, mp->b_wptr, pro_len); mp->b_wptr += pro_len; } return (mp); } /* * N_UNBIND_REQ * ------------------------------------------------------------------------- */ static inline mblk_t *n_unbind_req(void) { mblk_t *mp; N_unbind_req_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { 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); } return (mp); } /* * N_CONN_REQ * ------------------------------------------------------------------------- */ static inline mblk_t *n_conn_req(caddr_t dst_ptr, size_t dst_len, uint flags, caddr_t qos_ptr, size_t qos_len) { mblk_t *mp; N_conn_req_t *p; if ((mp = allocb(sizeof(*p) + dst_len + qos_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_conn_req_t *) mp->b_wptr; p->PRIM_type = N_CONN_REQ; p->DEST_length = dst_len; p->DEST_offset = dst_len ? sizeof(*p) : 0; p->CONN_flags = flags; p->QOS_length = qos_len; p->QOS_offset = qos_len ? sizeof(*p) + dst_len : 0; /* might pad */ mp->b_wptr += sizeof(*p); bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; bcopy(qos_ptr, mp->b_wptr, qos_len); mp->b_wptr += qos_len; } return (mp); } /* * N_CONN_RES * ------------------------------------------------------------------------- */ static inline mblk_t *n_conn_res(queue_t * q, caddr_t res_ptr, size_t res_len, uint flags, caddr_t qos_ptr, size_t qos_len) { mblk_t *mp; N_conn_res_t *p; if ((mp = allocb(sizeof(*p) + res_len + qos_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_conn_res_t *) mp->b_wptr; p->PRIM_type = N_CONN_RES; p->TOKEN_value = q; 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) : 0; mp->b_wptr += sizeof(*p); bcopy(res_ptr, mp->b_wptr, res_len); mp->b_wptr += res_len; bcopy(qos_ptr, mp->b_wptr, qos_len); mp->b_wptr += qos_len; } return (mp); } /* * N_DATA_REQ * ------------------------------------------------------------------------- */ static inline mblk_t *n_data_req(uint flags) { mblk_t *mp; N_data_req_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_data_req_t *) mp->b_wptr; p->PRIM_type = N_DATA_REQ; p->DATA_xfer_flags = flags; mp->b_wptr += sizeof(*p); } return (mp); } /* * N_DATACK_REQ * ------------------------------------------------------------------------- */ static inline mblk_t *n_datack_req(void) { mblk_t *mp; N_datack_req_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_datack_req_t *) mp->b_wptr; p->PRIM_type = N_DATACK_REQ; mp->b_wptr += sizeof(*p); } return (mp); } /* * N_EXDATA_REQ * ------------------------------------------------------------------------- */ static inline mblk_t *n_exdata_req(void) { mblk_t *mp; N_exdata_req_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_exdata_req_t *) mp->b_wptr; p->PRIM_type = N_EXDATA_REQ; mp->b_wptr += sizeof(*p); } return (mp); } /* * N_RESET_REQ * ------------------------------------------------------------------------- */ static inline mblk_t *n_reset_req(int reason) { mblk_t *mp; N_reset_req_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_reset_req_t *) mp->b_wptr; p->PRIM_type = N_RESET_REQ; p->RESET_reason = reason; mp->b_wptr += sizeof(*p); } return (mp); } /* * N_RESET_CON * ------------------------------------------------------------------------- */ static inline mblk_t *n_reset_con(void) { mblk_t *mp; N_reset_con_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_reset_con_t *) mp->b_wptr; p->PRIM_type = N_RESET_CON; mp->b_wptr += sizeof(*p); } return (mp); } /* * N_DISCON_REQ * ------------------------------------------------------------------------- */ static inline mblk_t *n_discon_req(int reason, caddr_t res_ptr, size_t res_len, uint seq) { mblk_t *mp; N_discon_req_t *p; if ((mp = allocb(sizeof(*p) + res_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_discon_req_t *) mp->b_wptr; p->PRIM_type = N_DISCON_REQ; p->DISCON_reason = reason; p->RES_length = res_len; p->RES_offset = res_len ? sizeof(*p) : 0; p->SEQ_number = seq; mp->b_wptr += sizeof(*p); bcopy(res_ptr, mp->b_wptr, res_len); mp->b_wptr += res_len; } return (mp); } /* * N_UNITDATA_REQ * ------------------------------------------------------------------------- */ static inline mblk_t *n_unitdata_req(caddr_t dst_ptr, size_t dst_len) { mblk_t *mp; N_unitdata_req_t *p; if ((mp = allocb(sizeof(*p) + dst_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (N_unitdata_req_t *) mp->b_wptr; p->PRIM_type = N_DISCON_REQ; p->DEST_length = dst_len; p->DEST_offset = dst_len ? sizeof(*p) : 0; p->RESERVED_field[0] = 0; p->RESERVED_field[1] = 0; mp->b_wptr += sizeof(*p); bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } return (mp); } /* * ========================================================================= * * SSCOP --> SSCOP Peer Primitives (Send Messages) * * ========================================================================= */ /* * ========================================================================= * * SSCOP State Machine TIMEOUTS * * ========================================================================= */ /* * ========================================================================= * * SSCOP Peer --> SSCOP Primitives (Received Messages) * * ========================================================================= */ /* * RECV SSCOP MESSAGE */ static int sscop_recv_msg(q, mp) const queue_t *q; const mblk_t *mp; { struct sscop *sp = SSCOP_PRIV(q); int err = -EMSGSIZE; if (!mp) return -EFAULT; if (mp->b_cont) do { uint s_state, type; if ((s_state = sp->s_state) >= SSCOP_MAX_STATES) s_state = 0; type = ((struct sscopchdr *) mp->b_cont->b_rptr)->type & SSCOP_CTYPE_MASK; if (0 < type && type <= SSCOP_SHUTDOWN_COMPLETE) err = (*sscop_states[s_state][type]) (q, mp); else err = sscop_recv_unrec_ctype(q, mp); } while (err > 0 && mp->b - cont); return (err); } /* * ========================================================================= * * N-Provider (IP) --> N-User (SSCOP) Primitives (M_PROTO, M_PCPROTO) * * ========================================================================= */ /* * N_UNITDATA_IND * N_UDERROR_IND */ static int ip_unitdata_ind(queue_t * q, mblk_t * pdu) static int ip_uderror_ind(queue_t * q, mblk_t * pdu) static int (*ip_prim[]) (queue_t *, mblk_t *) = { NULL, /* N_CONN_REQ 0 */ NULL, /* N_CONN_RES 1 */ NULL, /* N_DISCON_REQ 2 */ NULL, /* N_DATA_REQ 3 */ NULL, /* N_EXDATA_REQ 4 */ NULL, /* N_INFO_REQ 5 */ NULL, /* N_BIND_REQ 6 */ NULL, /* N_UNBIND_REQ 7 */ NULL, /* N_UNITDATA_REQ 8 */ NULL, /* N_OPTMGMT_REQ 9 */ NULL, /* not used 10 */ NULL, /* N_CONN_IND 11 */ NULL, /* N_CONN_CON 12 */ NULL, /* N_DISCON_IND 13 */ NULL, /* N_DATA_IND 14 */ NULL, /* N_EXDATA_IND 15 */ NULL, /* N_INFO_ACK 16 */ NULL, /* N_BIND_ACK 17 */ NULL, /* N_ERROR_ACK 18 */ NULL, /* N_OK_ACK 19 */ &ip_unitdata_ind, /* N_UNITDATA_IND 20 */ &ip_uderror_ind, /* N_UDERROR_IND 21 */ NULL, /* not used 22 */ NULL, /* N_DATACK_REQ 23 */ NULL, /* N_DATACK_IND 24 */ NULL, /* N_RESET_REQ 25 */ NULL, /* N_RESET_IND 26 */ NULL, /* N_RESET_RES 27 */ NULL /* N_RESET_CON 28 */ }; /* * ========================================================================= * * N-User --> N-Provider (SSCOP) Primitives (M_CTL, M_PROTO, M_PCPROTO) * * ========================================================================= */ static int n_info_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; (void) pdu; if (!(mp = n_info_ack(q))) return (-ENOBUFS); freemsg(pdu); qreply(q, mp); return (0); } static int t_optmgmt_req(queue_t * q, mblk_t * pdu) { int err; mblk_t *mp; sscop_t *sp = (sscop_t *) q->q_ptr; if (sp->n_state != NS_IDLE) goto opmgmt_req_outstate; { struct T_optmgmt_req *p = (struct T_optmgmt_req *) pdu->b_rptr; const caddr_t opt_ptr = p->OPT_offset + pdu->b_rptr; const size_t opt_len = p->OPT_length; const uint flags = p->MGMT_flags; } optmgmt_req_outstate: err = NOUTSTATE; goto optmgmt_req_error; optmgmt_req_error: freemsg(pdu); qreply(q, t_error_ack(T_OPTMGMT_REQ, err)); return (0); } static int t_bind_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; sscop_t *sp = SSCOP_PRIV(q); switch (sp->n_state) { case NS_UNBND: { struct T_bind_req *p = (struct T_bind_req *) pdu->b_rptr; caddr_t add_ptr; size_t add_len; u16 bport = 0; u32 baddr; int err = 0; struct sscop_bind *bb, **bbp; /* pull addresses out of bind primitive */ if ((add_len = p->ADDR_length) >= sizeof(u16) && add_len <= mp->b_wptr - p->ADDR_offset) { struct sscop_baddr **sbp = &sp->baddr; add_ptr = p->ADDR_offset + pdu->b_rptr; bport = *((u16 *) add_ptr)++; for (add_len -= sizeof(u16); add_len >= sizeof(u32); add_len -= sizeof(u32)) { if (!((baddr = *((u32 *) add_ptr)++) & 0xff000000)) goto t_bind_req_badaddr; if (! (*sbp = kmem_alloc_cache(sscop_badd_cachep, SLAB_HWCACHE_ALIGN))) goto t_bind_req_nomem; (*sbp)->baddr = baddr; (*sbp)->next = NULL; (*sbp)->pprev = sbp; sbp = &(*sbp)->next; sp->banum++; } } cons = p->CONIND_number; /* See if we need to assign a port number */ if (!(sp->bport = htons(bport)) && !(sp->cons = cons)) { static u16 sscop_port_rover = 0; /* * This stream can only be used for outgoing connections, so * if it is assigned a zero port number we choose an unused * port number for the stream and assign it. */ int low = sysctl_local_port_range[0]; int high = sysctl_local_port_range[1]; int rem = (high - low) + 1; bport = sscop_port_rover; for (; rem > 0; bport++, rem--) { if (bport > high || bport < low) bport = low; bbp = &sscop_bind_hash[bport & 0xff]; for (bb = *bbp; bb && bb->port != bport; bb = bb->next); if (!bb || !bb->bound) break; } /* want a position with an unowned bind bucket */ if (rem <= 0 || (bb && bb->bound)) goto t_bind_req_noaddr; sscop_port_rover = bport; } else { bbp = &sscop_bind_hash[bport & 0xff]; for (bb = *bbp; bb && sp->bport != bport; bb = bb->next); } /* If we have an existing bind bucket, check for conflicts */ if (bb && bb->cons && cons) { struct sscop *sp2; err = TADDRBUSY; for (sp2 = bb->bound; sp2; sp2 = sp2->bind_next) { struct sscop_baddr *sb, *sb2; if (!sp->baddr && !sb2->baddr) goto t_bind_req_addrbusy; for (sb = sp->baddr; sb; sb = sb->next) for (sb2 = sp2->baddr; sb2; sb2 = sb2->next) if (sb->baddr == sb2->baddr) goto t_bind_req_addrbusy; } } /* place in bind hashes */ if (!bb) { /* no bind bucket, create one */ if (!(bb = kmalloc(sizeof(*bb), GFP_ATOMIC))) { freemsg(mp); goto t_bind_req_nomem; } if ((bb->next = *bbp)) bb->next->pprev = &bb->next; bb->pprev = bbp; *bbp = bb; bb->port = bport; bb->cons = 0; bb->bound = NULL; } sp->bind = bb; if ((sp->bind_next = bb->bound)) sp->bind_next->bind_pprev = &sp->bind_next; sp->bind_pprev = &bb->bound; bb->bound = sp; bb->cons++; sp->n_state = NS_IDLE; freemsg(pdu); mp = t_bind_ack(q); qreply(q, mp); return (0); } } t_bind_req_outstate: err = NOUTSTATE; goto t_bind_req_error; t_bind_req_badaddr: err = NBADADDR; goto t_bind_req_error; t_bind_req_addrbusy: err = TADDRBUSY; goto t_bind_req_error; t_bind_req_noaddr: err = NNOADDR; goto t_bind_req_error; t_bind_req_nomem: err = -ENOMEM; goto t_bind_req_error; t_bind_req_nobufs: err = -ENOBUFS; goto t_bind_req_error; t_bind_req_error: sp->bport = 0; { struct sscop_baddr *sb; while ((sb = sp->baddr)) { sb->baddr = sb->next; kmem_free_cache(sscop_badd_cachep, sb); } sp->banum = 0; sp->baddr = NULL; } freemsg(pdu); qreply(q, t_error_ack(T_BIND_REQ, err)); return (0); } static int t_unbind_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; sscop_t *sp = SSCOP_PRIV(q); struct sscop_bind *bb; if (sp->n_state != NS_IDLE) goto t_unbind_req_outstate; if ((bb = sp->bind)) { if (sp->cons) bb->cons--; if ((*sp->bind_pprev = sp->bind_next)) sp->bind_next->bind_pprev = sp->bind_pprev; sp->bind = NULL; sp->bind_next = NULL; sp->bind_pprev = NULL; sp->cons = 0; if (!bb->bound) { if ((*bb->pprev = bb->next)) bb->next->pprev = bb->pprev; kfree(bb); } } { struct sscop_baddr *sb; while ((sb = sp->baddr)) { sp->baddr = sb->next; kmem_free_cache(sscop_badd_cachep, sb); } sp->banum = 0; sp->bport = 0; } sp->n_state = NS_UNBND; freemsg(pdu); mp = t_ok_ack(T_UNBIND_REQ); qreply(q, mp); return (0); t_unbind_req_outstate: err = NOUTSTATE; goto t_unbind_req_error; t_unbind_req_error: freemsg(pdu); qreply(q, t_error_ack(T_UNBIND_REQ, err)); return (0); } static int t_conn_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; sscop_t *sp = SSCOP_PRIV(q); struct T_conn_req *p = (struct T_conn_req *) pdu->b_rptr; const caddr_t dst_ptr = p->DEST_offset + pdu->b_rptr; const size_t dst_len = p->DEST_length; const caddr_t opt_ptr = p->OPT_offset + pdu->b_rptr; const size_t opt_len = p->OPT_length; if (sp->n_state != NS_IDLE) goto t_conn_req_outstate; /* pull addresses out of the destination */ if ((dst_len = p->DEST_length) < sizeof(u16) + sizeof(u32)) goto t_conn_req_badaddr; if ((dst_len > mp->b_wptr - p->DEST_offset)) goto t_conn_req_badaddr; dst_ptr = p->DEST_offset + pdu->b_rptr; if (!(sp->dport = htons(*((u16 *) dst_ptr)++))) goto t_conn_req_badaddr; /* * TODO: check if user is allowed to set port address. */ for (dst_len -= sizeof(u16); dst_len >= sizeof(u32); dst_len -= siseof(u32)) { struct sscop_daddr *sd; if (!((daddr = *((u32 *) dst_ptr)++) & 0xff000000)) goto t_conn_req_badaddr; if (!(sd = kmalloc(sizeof(*sd), GFP_ATOMIC))) goto t_conn_req_nomem; bzero(sd, sizeof(*sd)); if ((sd->next = sp->daddr)) sd->next->pprev = &sd->next; sd->pprev = &sp->daddr; sp->daddr = sd; sp->danum++; sd->sp = sp; sd->daddr = daddr; /* * TODO: initialize reset of daddr structure. */ } /* * TODO: check if we can route to one of the destination addresses. */ sp->v_tag = tcp_secure_sequence(sp->saddr->saddr, sp->daddr->daddr, sp->sport, sp->dport); sp->a_rwnd = FIXME; sp->n_ostr = -1; /* ask for as many as we can get */ sp->n_istr = -1; /* offer as many as the other end needs */ sp->i_tsn = htonl(sp->v_tag); /* * TODO: pull out options, this will wind up negotiating the number * if outbound streams and the accepted number of inbound streams * requested by the user. It may also set the advertized receive * window, but I would rather gather that from the high water mark on * the upstream receive queue. We must pick an initial verification * tag and an initial transmit sequence number. */ /* * TODO: do some connect routing on the destination address list and * determine the best alternative addresses. */ sp->taddr = sp->daddr; sp->raddr = sp->daddr->next ? sp->daddr->next : sp->daddr; if ((err = sscop_send_init(p))) goto t_conn_req_error; sp->s_state = SSCOP_COOKIE_WAIT; sp->n_state = T_WCON_CREQ; freemsg(pdu); mp = t_ok_ack(T_CONN_REQ); qreply(q, mp); return (0); /* Try to connect to the first address */ t_conn_req_outstate: err = NOUTSTATE; goto t_conn_req_error; t_conn_req_badaddr: err = NBADADDR; goto t_conn_req_error; t_conn_req_error: bzero(&sp->l, sizeof(sp->l)); { struct sscop_daddr *sd, sd_next = sp->daddrs; while ((sd = sd_next)) { sd_next = sd->next; kfree(sd); } sp->danum = 0; } freemsg(pdu); qreply(q, t_error_ack(T_CONN_REQ, err)); return (0); } static int t_conn_res(queue_t * q, mblk_t * pdu) { mblk_t *mp; sscop_t *sp = SSCOP_PRIV(q); struct T_conn_res *p = (struct T_conn_res *) pdu->b_rptr; queue_t *aq = (queue_t *) p->ACCEPTOR_id; const caddr_t opt_ptr = p->OPT_offset + pdu->b_rptr; const size_t opt_len = p->OPT_length; const uint seq = p->SEQ_number; switch (sp->n_state) { case NS_WRES_CIND: { struct sscop *ap; struct sscop_cookie_echo *m; struct sscop_cookie *ck /* first look for connection indication with indicated sequence number */ for (mp = sp->connect_queue.q_head; mp; mp = mp->b_next) { struct sscop_rcb *cb = SSCOP_RCB(mp); if (cb->seq == seq) break; } if (!mp) goto conn_res_badseq; if (!aq || !aq->q_ptr || !aq->q_qinfo || !aq->q_qinfo->qi_minfo) goto conn_res_badq; if (aq->q_qinfo->qi_minfo.mi_idnum != q->q_qinfo->qi_minfo.mi_idnum) goto conn_res_provmismatch; ap = SSCOP_PRIV(aq); if (aq != q) { if (aq->n_state == NS_UNBND) goto conn_res_badaddr; if (aq->n_state != NS_IDLE) goto conn_res_badf; if (ap->cons) goto conn_res_resqlen; if (sp->sport && ap->sport && sp->sport != ap->sport) goto conn_res_resaddr; if (sp->saddr && ap->saddr && !sscop_same_bind(sp, ap)) goto conn_res_resaddr; } if (opt_len) { u16 *sidp = NULL; u32 *ppip = NULL; struct t_opthdr *oh; caddr_t op; for (op = opt_ptr, oh = (struct t_opthdr *) op; op < opt_ptr + opt_len; op += PADC(oh->length), oh = (struct t_opthdr *) op) { if (oh->level == T_INET_SSCOP) { switch (oh->name) { case SSCOP_SID: if (oh->length >= sizeof(*oh) + sizeof(u16)) sidp = ((u16 *) oh->value); continue; case SSCOP_PPI: if (oh->length >= sizeof(*oh) + sizeof(u32)) ppip = ((u32 *) oh->value); continue; default: goto conn_res_badopt; } } goto conn_res_badopt; } if (sidp) ap->sid = *sidp; if (ppip) ap->ppi = *ppip; ap->ostr = sscop_find_ostr(ap, ap->sid); ap->ostr->ppi = ap->ppi; } m = (struct sscop_cookie_echo *) mp->b_cont->b_rptr; ck = m->cookie; ap->s_state = SSCOP_ESTABLISHED; ap->n_state = NS_DATA_XFER; ap->dport = ck->dport; ap->sport = ck->sport; ap->sackf = SSCOP_SACKF_NOD; ap->v_tag = ck->v_tag; ap->p_tag = ck->p_tag; ap->i_strs = ck->i_strs; ap->o_strs = ck->o_strs; ap->sscop_tsn = ck->v_tag; ap->a_tsn = ck->v_tag - 1; ap->c_tsn = ck->p_tsn - 1; ap->e_tsn = ck->p_tsn - 1; ap->p_rwnd = ck->p_rwnd; while ((st = ap->ostrm)) { ap->ostrm = st->next; kmem_free_cache(sscop_strm_cachep, st); } ap->osnum = 0; while ((st = ap->istrm)) { ap->istrm = st->next; kmem_free_cache(sscop_strm_cachep, st); } ap->isnum = 0; while ((sd = ap->daddr)) { ap->daddr = sd->next; kmem_free_cache(sscop_dest_cachep, sd); } ap->danum = 0; while ((ss = ap->saddr)) { ap->saddr = ss->next; kmem_free_cache(sscop_srce_cachep, ss); } ap->sanum = 0; { int dnum = ck->dta_num; int snum = ck->sta_num; struct sscop_daddr **sdp = &ap->daddr; struct sscop_saddr **ssp = &ap->saddr; u32 *addp = (u32 *) (((u8 *) (ck + 1) + ck->opt_len)); for (i = 0; i < ck->dta_num; i++, sdp = &(*sdp)->next) { if (! (*sdp = kmem_alloc_cache(sscop_dest_cachep, SLAB_HWALIGN_CACHE))) goto conn_res_nomem; bzero(*sdp, sizeof(**sdp)); (*sdp)->daddr = *addp++; (*sdp)->sp = ap; /* * More daddr initialization. */ } for (i = 0; i < ck->sta_num; i++, ssp = (*ssp)->next) { if (! (*ssp = kmem_alloc_cache(sscop_srce_cachep, SLAB_HWALIGN_CACHE))) goto conn_res_nomem; bzero(*ssp, sizeof(**ssp)); (*ssp)->saddr = *addp++; (*ssp)->sp = ap; /* * More saddr initialization. */ } } } } conn_res_outstate: err = NOUTSTATE; goto conn_res_error; conn_res_nomem: err = -ENOMEM; goto conn_res_free_error; conn_res_badaddr: err = NBADADDR; goto conn_res_error; conn_res_resaddr: err = TRESADDR; goto conn_res_error; conn_res_badseq: err = NBADSEQ; goto conn_res_error; conn_res_badf: err = NBADTOKEN; goto conn_res_error; conn_res_badopt: err = NBADOPT; goto conn_res_error; conn_res_provmismatch: err = TPROVMISMATCH; goto conn_res_error; conn_res_resqlen: err = TRESQLEN; goto conn_res_error; conn_res_free_error: while ((sd = ap->daddr)) { ap->daddr = sd->next; kmem_free_cache(sscop_dest_cachep, sd); } ap->danum = 0; ap->dport = 0; while ((ss = ap->daddr)) { ap->daddr = ss->next; kmem_free_cache(sscop_srce_cachep, ss); } ap->sanum = 0; conn_res_error: freemsg(pdu); qreply(q, t_error_ack(T_CONN_RES, NOUTSTATE)); return (0); } static int t_data_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; sscop_t *sp = SSCOP_PRIV(q); struct T_data_req *p = (struct T_data_req *) pdu->b_rptr; const uint flag = p->MORE_flag; uint flags = 0; switch (sp->n_state) { case NS_DATA_XFER: case NS_WREQ_ORDREL: if (!pdu->b_cont || pdu->b_cont->b_datap->db_type != M_DATA) goto data_req_error; if (sp->ostr->x_more) goto data_req_error; if (!sp->ostr->n_more) flags |= SSCOPCB_FLAG_FIRST_FRAG; if (!(sp->ostr->n_more = (flag & T_MORE))) flags |= SSCOPCB_FLAG_LAST_FRAG; if ((err = sscop_send_data(sp, NULL, sp->ostr, flags, pdu->b_cont))) return (err); freeb(pdu); return (0); case NS_IDLE: freemsg(pdu); return (0); } data_req_error: freemsg(pdu); qreply(q, t_m_error(EPROTO)); return (0); } static int t_exdata_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; sscop_t *sp = SSCOP_PRIV(q); struct T_exdata_req *p = (struct T_exdata_req *) pdu->b_rptr; const uint flag = p->MORE_flag; uint flags = SSCOPCB_FLAG_URG; switch (sp->n_state) { case NS_DATA_XFER: case NS_WREQ_ORDREL: if (!pdu->b_cont || pdu->b_cont->b_datap->db_type != M_DATA) goto exdata_req_error; if (!sp->ostr->x_more) flags |= SSCOPCB_FLAG_FIRST_FRAG; if (!(sp->ostr->x_more = (flag & T_MORE))) flags |= SSCOPCB_FLAG_LAST_FRAG; if ((err = sscop_send_data(sp, NULL, sp->ostr, flags pdu->b_cont))) return (err); freeb(pdu); return (0); case NS_IDLE: freemsg(pdu); return (0); } freemsg(pdu); qreply(q, t_m_error(EPROTO)); return (0); } static int t_optdata_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; sscop_t *sp = SSCOP_PRIV(q); struct T_optdata_req *p = (struct T_optdata_req *) pdu->b_rptr; const uint flag = p->DATA_flag; const caddr_t opt_ptr = p->OPT_offset + pdu->b_rptr; const size_t opt_len = p->OPT_length; if (!opt_len) return (flag & T_EXPEDITED) ? t_exdata_req(q, pdu) : t_data_req(q, pdu); switch (sp->n_state) { case NS_DATA_XFER: case NS_WREQ_ORDREL: { uint flags = 0; struct sscop_strm *st = sp->ostr; uint *morep; { int sid = -1; int ppi = -1; caddr_t op; struct t_opthdr *oh; /* Walk through options */ for (op = opt_ptr, oh = (struct t_opthdr *) op; op < opt_ptr + opt_len; op += PADC(oh->length), oh = (struct t_opthdr *) op) { switch (oh->level) { case T_INET_SSCOP: switch (oh->name) { case SSCOP_SID: if (oh->length >= sizeof(*oh) + sizeof(u16)) sid = *((u16 *) oh->value); continue; case SSCOP_PPI: if (oh->length >= sizeof(*oh) + sizeof(u32)) ppi = *((u32 *) oh->value); continue; } } } if (sid != -1 && !(st = sscop_find_ostr(sp, sid)) && !(st = sp->ostr) && !(st = sp->ostr = sscop_find_ostr(sp, sp->sid)) && !(st = sp->ostr = sscop_find_ostr(sp, 0)) && !(st = sp->ostr = sscop_alloc_ostr(sp, 0))) return -ENOMEM; if (ppi != -1) st->ppi = ppi; } if (flag & T_EXPEDITED) { flags |= SSCOPCB_FLAG_URG; morep = &st->x_more; } else morep = &st->n_more; if (!pdu->b_cont || pdu->b_cont->b_datap->db_type != M_DATA) goto optdata_req_error; if (!*morep) flags |= SSCOPCB_FLAG_FIRST_FRAG; if (!(*morep = (flag & T_MORE))) flags |= SSCOPCB_FLAG_LAST_FRAG; if ((err = sscop_send_data(sp, NULL, st, flags, pdu->b_cont))) return (err); freeb(pdu); return (0); } case NS_IDLE: freemsg(pdu); return (0); } freemsg(pdu); qreply(q, t_m_error(EPROTO)); return (0); } static int t_unitdata_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; sscop_t *sp = SSCOP_PRIV(q); struct T_unitdata_req *p = (struct T_unitdata_req *) pdu->b_rptr; const caddr_t dst_ptr = p->DEST_offset + pdu->b_rptr; const size_t dst_len = p->DEST_length; const caddr_t opt_ptr = p->OPT_offset + pdu->b_rptr; const size_t opt_len = p->OPT_length; freemsg(pdu); qreply(q, t_error_ack(T_UNIDATA_REQ, NNOTSUPPORT)); return (0); } static int t_discon_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; sscop_t *sp = SSCOP_PRIV(q); struct T_discon_req *p = (struct T_discon_req *) pdu->b_rptr; const uint seq = p->SEQ_number; if (sp->n_state == NS_WRES_CIND) { mblk_t *mp; for (mp = sp->connect_queue.q_head; mp; mp->next) { struct sscop_rcb *cb = SSCOP_RCB(mp); if (cb->seq == seq) break; } if (!mp) goto discon_res_badseq; bufq_unlink(&sp->connect_queue, mp); freemsg(pdu); qreply(q, t_ok_ack(T_DISCON_REQ)); return (0); } if ((1 << sp-> n_state) & (TSF_WCON_CREQ | TSF_DATA_XFER | TSF_WIND_ORDREL | TSF_WREQ_ORDREL)) { struct sscop_daddr *sd; struct sscop_saddr *ss; /* stop association timers */ if (sp->timer_cookie) untimeout(xchg(&sp->timer_cookie, 0)); if (sp->timer_sack) untimeout(xchg(&sp->timer_sack, 0)); freemsg(xchg(&sp->retry, NULL)); /* send abort */ s_send_abort(sp); /* remove from vtag cache */ if (*sp->vtag_pprev = sp->vtag_next) sp->vtag_next->vtag_pprev = sp->vtag_pprev; sp->vtag_next = NULL; sp->vtag_pprev = &sp->vtag_next; sp->v_tag = 0; /* remove from ptag cache */ if (*sp->ptag_pprev = sp->ptag_next) sp->ptag_next->ptag_pprev = sp->ptag_pprev; sp->ptag_next = NULL; sp->ptag_pprev = &sp->ptag_next; sp->p_tag = 0; /* remove peer addresses */ while ((sd = sp->daddr)) { sp->daddr = sd->next; if (sd->timer_retrans) untimeout(xchg(&sd->timer_retrans, 0)); if (sd->timer_idle) untimeout(xchg(&sd->timer_idle, 0)); if (sd->timer_heartbeat) untimeout(xchg(&sd->timer_heartbeat, 0)); /* FIXME: free destination cache */ kmem_free_cache(sscop_dest_cachep, sd); } sp->danum = 0; /* remove local addresses */ while ((ss = sp->saddr)) { sp->saddr = ss->next; kmem_free_cache(sscop_srce_cachep, ss): } sp->sanum = 0; /* purge send queues */ bufq_purge(&sp->write_queue); bufq_purge(&sp->urgent_queue); bufq_purge(&sp->retrans_queue); /* purge recv queues */ bufq_purge(&sp->out_of_order_queue); bufq_purge(&sp->duplicate_queue); sp->ngaps = 0; sp->ndups = 0; sp->s_state = SSCOP_CLOSED; sp->n_state = T_IDLE; freemsg(pdu); qreply(q, t_ok_ack(T_DISCON_REQ)); return (0); } discon_req_outstate: err = NOUTSTATE; goto discon_req_error; discon_req_badseq: err = NBADSEQ; goto discon_req_error; discon_req_error: freemsg(pdu); qreply(q, t_error_ack(T_DISCON_REQ, err)); return (0); } static int t_ordrel_req(queue_t * q, mblk_t * pdu) { sscop_t *sp = SSCOP_PRIV(q); struct T_ordrel_req *p = (struct T_ordrel_req *) pdu->b_rptr; switch (sp->n_state) { case NS_DATA_XFER: ensure(sp->s_state == SSCOP_ESTABLISHED, break); freemsg(pdu); sp->s_state = SSCOP_SHUTDOWN_PENDING; if (!sp->retrans_queue.q_count) { sscop_send_shutdown(sp); sp->s_state = SSCOP_SHUTDOWN_SENT; } sp->n_state = NS_WIND_ORDREL; return (0); case NS_WREQ_ORDREL: ensure(sp->s_state == SSCOP_SHUTDOWN_RECEIVED, break); freemsg(pdu); if (!sp->retrans_queue.q_count) { sscop_send_shutdown_ack(sp); sp->s_state = SSCOP_SHUTDOWN_ACK_SENT2; } sp->n_state = NS_IDLE; return (0); } sp->n_state = NS_NOSTATES; freemsg(pdu); qreply(q, t_m_error(EPROTO)); return (0); } static int (*t_prim[]) (queue_t *, mblk_t *) = { &t_conn_req, /* T_CONN_REQ 0 */ &t_conn_res, /* T_CONN_RES 1 */ &t_discon_req, /* T_DISCON_REQ 2 */ &t_data_req, /* T_DATA_REQ 3 */ &t_exdata_req, /* T_EXDATA_REQ 4 */ &t_info_req, /* T_INFO_REQ 5 */ &t_bind_req, /* T_BIND_REQ 6 */ &t_unbind_req, /* T_UNBIND_REQ 7 */ &t_unitdata_req, /* T_UNITDATA_REQ 8 */ &t_optmgmt_req, /* T_OPTMGMT_REQ 9 */ &t_ordrel_req, /* T_ORDREL_REQ 10 */ NULL, /* T_CONN_IND 11 */ NULL, /* T_CONN_CON 12 */ NULL, /* T_DISCON_IND 13 */ NULL, /* T_DATA_IND 14 */ NULL, /* T_EXDATA_IND 15 */ NULL, /* T_INFO_ACK 16 */ NULL, /* T_BIND_ACK 17 */ NULL, /* T_ERROR_ACK 18 */ NULL, /* T_OK_ACK 19 */ NULL, /* T_UNITDATA_IND 20 */ NULL, /* T_UDERROR_IND 21 */ NULL, /* T_OPTMGMT_ACK 22 */ NULL, /* T_ORDREL_IND 23 */ &t_optdata_req, /* T_OPTDATA_REQ 24 */ &t_addr_req, /* T_ADDR_REQ 25 */ NULL, /* T_OPTDATA_IND 26 */ NULL /* T_ADDR_ACK 27 */ }; /* * ========================================================================= * * SSCOP IOCTLs * * ========================================================================= */ static int (*sscop_ioctl[]) (queue_t *, uint, void *) = { }; /* * ========================================================================= * * SSCOP STREAMS Message Handling * * ========================================================================= * * M_DATA Handling * * ------------------------------------------------------------------------- */ static inline int sscop_w_data(queue_t * q, mblk_t * mp) { return sscop_write_data(q, mp); } static inline int sscop_r_data(queue_t * q, mblk_t * mp) { return sscop_recv_sdu(q, mp); } /* * ------------------------------------------------------------------------- * * M_PROTO, M_PCPROTO Handling * * ------------------------------------------------------------------------- */ static inline int sscop_w_proto(queue_t * q, mblk_t * mp) { int prim = ((union T_primitives *) (mp->b_rptr))->type; if (0 <= prim && prim < sizeof(t_prim) / sizeof(void *)) if (t_prim[prim]) return (*t_prim[prim]) (q, mp); return (-EOPNOTSUPP); } static inline int sscop_r_proto(queue_t * q, mblk_t * mp) { int prim = ((union N_primitives *) (mp->b_rptr))->type; if (0 <= prim && prim < sizeof(n_prim) / sizeof(void *)) if (n_prim[prim]) return (*n_prim[prim]) (q, mp); return (-EOPNOTSUPP); } static inline int sscop_w_pcproto(queue_t * q, mblk_t * mp) { return sscop_w_proto(q, mp); } static inline int sscop_r_pcproto(queue_t * q, mblk_t * mp) { return sscop_r_proto(q, mp); } /* * ------------------------------------------------------------------------- * * M_CTL Handling * * ------------------------------------------------------------------------- */ static inline int sscop_w_ctl(queue_t * q, mblk_t * mp) { (void) q; (void) mp; return (-EOPNOTSUPP); } static inline int sscop_r_ctl(queue_t * q, mblk_t * mp) { (void) q; (void) mp; return (-EOPNOTSUPP); } /* * ------------------------------------------------------------------------- * * M_IOCTL Handling * * ------------------------------------------------------------------------- */ static inline int sscop_w_ioctl(queue_t * q, mblk_t * mp) { int ret; struct iocblk *iocp = (struct iocblk *) mp->b_rptr; int cmd = iocp->ioc_cmd; void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL; int type = _IOC_TYPE(cmd); int nr = _IOC_NR(cmd); int size = _IOC_SIZE(cmd); switch (type) { case __SID: switch (cmd) { case I_LINK: case I_PLINK: case I_UNLINK: case I_PUNLINK: { struct linkblk *lp = (struct linkblk *) arg; (void) lp; } default: case I_FDINSERT: ret = -EINVAL; } break; case SSCOP_IOC_MAGIC: if (iocp->ioc_count >= size) { if (0 <= nr && nr < sizeof(sscop_ioctl) / sizeof(void *)) { if (sscop_ioctl[nr]) ret = (*sscop_ioctl[nr]) (q, cmd, arg); else ret = -EOPNOTSUPP; } } else return = -EINVAL; break; default: if (q->q_next) { putnext(q, mp); return (0); } ret = -EOPNOTSUPP; } iocp->ioc_error = -ret; iocp->ioc_rval = ret ? -1 : 0; qrepy(q, mp); return (0); } /* * ------------------------------------------------------------------------- * * M_FLUSH Handling * * ------------------------------------------------------------------------- */ static inline void sscop_w_flush(queue_t * q, mblk_t * mp) { if (*mp->b_rptr & M_FLUSHW) { flushq(q, FLUSHALL); if (q - q_next) { putnext(q, mp); return; } *mp->b_rptr &= ~FLUSHW; } if (*mp->b_rptr & FLUSHR) { flushq(RD(q), FLUSHALL); qreply(q, mp); return; } if (q->q_next) { putnext(q, mp); return; } } /* * ------------------------------------------------------------------------- * * M_ERROR Handling * * ------------------------------------------------------------------------- */ static inline void sscop_r_error(queue_t * q, mblk_t * mp) { sscop_t *sp = SSCOP_PRIV(q); sp->zapped = 1; if (q->q_next) { putnext(q, mp); return; } } /* * ========================================================================= * * STREAMS QUEUE PUT and QUEUE SERVICE routines * * ========================================================================= * * READ QUEUE PUT and SRV routines * * ------------------------------------------------------------------------- * * SSCOP RPUT - Message from below. * * If the message is a priority message we attempt to process it immediately. * If the message is a non-priority message, but there are no messages on the * queue yet, we attempt to process it immediately. If the message is not * supported, we pass it down-queue if possible. If the message cannot be * processed immediately we place it on the queue. */ static INT sscop_rput(queue_t * q, mblk_t * mp) { int err = -EOPNOTSUPP; if (mp->b_datap->db_type < QPCTL && q->q_count) { putq(q, mp); /* * NOTE:- after placing messages on the queue here, I should * check if placing the message on the queue crosses a band * threshold for congestion accept and congestion discard. * When crossing congestion accept, I should sent busy to the * peer and notify MTP3. When crossing congestion discard I * should notify MTP3. */ return (0); } switch (mp->b_datap->db_type) { case M_DATA: if ((err = sscop_r_data(q, mp))) break; return (0); case M_PROTO: if ((err = sscop_r_proto(q, mp))) break; return (0); case M_PCPROTO: if ((err = sscop_r_pcproto(q, mp))) break; return (0); case M_CTL: if ((err = sscop_r_ctl(q, mp))) break; return (0); case M_ERROR: sscop_r_error(q, mp); return (0); } switch (err) { case -EAGAIN: putq(q, mp); return (0); case -EOPNOTSUPP: if (q->q_next) { putnext(q, mp); return (0); } } freemsg(mp); return (err); } /* * SSCOP RSRV - Queued message from below. * * If the message is a priority message we attempt to process it immediately * and without flow control. If the message is a non-priority message and * the next queue is flow controlled, we put the message back on the queue * and wait. If we cannot process a priority message immediately we cannot * place it back on the queue and discard it. We requeue non-priority * messages which cannot be processed immediately. Unrecognized messages are * passed down-queue. */ static INT sscop_rsrv(queue_t * q) { mblk_t *mp; int err = -EOPNOTSUPP; while ((mp = getq(q))) { if (mp->b_datap->db_type < QPCTL && !canputnext(q)) { putbq(q, mp); return (0); } switch (mp->b_datap->db_type) { case M_DATA: if ((err = sscop_r_data(q, mp))) break; goto rsrv_continue; case M_PROTO: if ((err = sscop_r_proto(q, mp))) break; goto rsrv_continue; case M_PCPROTO: if ((err = sscop_r_pcproto(q, mp))) break; goto rsrv_continue; case M_CTL: if ((err = sscop_r_ctl(q, mp))) break; goto rsrv_continue; } switch (err) { case -EAGAIN: if (mp->b_datap->db_type < QPCTL) { putbq(q, mp); return (0); } case -EOPNOTSUPP: if (q->q_next) { putnext(q, mp); goto rsrv_continue; } break; } freemsg(mp); rsrv_continue: /* * NOTE:- I have removed and processed a message from the * receive queue, so I should check for receive congestion * abatement. If receive congestion has abated below the * accept threshold, I should signal MTP that there is no * longer any receive congestion. */ continue; } return (0); } /* * ------------------------------------------------------------------------- * * WRITE QUEUE PUT and SRV routines * * ------------------------------------------------------------------------- * * SSCOP WPUT - Message from above. * * If the message is priority message we attempt to process it immediately. * If the message is non-priority message, but there are no messages on the * queue yet, we attempt to process it immediately. If the message is not * supported, we pass it down-queue if possible. If the message cannot be * processed immediately, we place it on the queue. */ static INT sscop_wput(queue_t * q, mblk_t * mp) { mblk_t *mp; int err = -EOPNOTSUPP; if (q->q_count && mp->b_datap->db_type < QPCTL) { putq(q, mp); /* * NOTE:- after placing messages on the queue here, I should * check for transmit congestion. I should check if placing * the message on the queue crosses a band threshold for * congestion onset and abatement. When crossing congestion * thresholds, I should notify MTP3. */ return (0); } switch (mp->b_datap->db_type) { case M_DATA: if ((err = sscop_w_data(q, mp))) break; return (0); case M_PROTO: if ((err = sscop_w_proto(q, mp))) break; return (0); case M_PCPROTO: if ((err = sscop_w_pcproto(q, mp))) break; return (0); case M_CTL: if ((err = sscop_w_ctl(q, mp))) break; return (0); case M_IOCTL: if ((err = sscop_w_ioctl(q, mp))) break; return (0); case M_FLUSH: sscop_w_flush(q, mp); return (0); } switch (err) { case -EAGAIN: if (mp->b_datap->db_type < QPCTL) { putq(q, mp); return (0); } case -EOPNOTSUPP: if (q->q_next) { putnext(q, mp); return (0); } } freemsg(mp); return (err); } /* * SSCOP WSRV = Queued message from above. * * If the message is a priority message we attempt to process it immediately * and without flow control. If the message is a non-priority message and * the next queue is flow controlled, we put the message back on the queue * and wait. If we cannot process a priority message immediately we cannot * place it back on the queue, we discard it. We requeue non-priority * messages which cannot be processed immediately. Unrecognized messages are * passed down-queue. */ static INT sscop_wsrv(queue_t * q) { int err = -EOPNOTSUPP; mblk_t *mp; while ((mp = getq(q))) { if (q->q_next && mp->b_datap->db_type < QPCTL && !canputnext(q)) { putbq(q, mp); return (0); } switch (mp->b_datap->db_type) { case M_DATA: if ((err = sscop_w_data(q, mp))) break; goto wsrv_continue; case M_PROTO: if ((err = sscop_w_proto(q, mp))) break; goto wsrv_continue; case M_PCPROTO: if ((err = sscop_w_pcproto(q, mp))) break; goto wsrv_continue; case M_CTL: if ((err = sscop_w_ctl(q, mp))) break; goto wsrv_continue; } switch (err) { case -EAGAIN: if (mp->b_datap->db_type < QPCTL) { putbq(q, mp); return (0); } case -EOPNOTSUPP: if (q->q_next) { putnext(q, mp); goto wsrv_continue; } } freemsg(mp); wsrv_continue: /* * NOTE:- I have removed a message from the queue, so I * should check for band congestion abatement for the data * band to see if transmit congestion has abated. If it has, * I should notify MTP3 of transmit abatement. */ continue; } return (0); } /* * ========================================================================= * * OPEN and CLOSE * * ========================================================================= */ /* * Private structure allocation and deallocation. * * We use Linux hardware aligned cache here for speedy access to information * contained in the private data structure. */ kmem_cache_t *sscop_cachep = NULL; static sscop_t *sscop_alloc_priv(queue_t * q) { sscop_t *sscop; if ((sscop = kmem_cache_alloc(sscop_cachep))) { bzero(sscop, sizeof(*sscop)); RD(q)->q_ptr = WR(q)->q_tpr = sscop; sscop->rq = RD(q); sscop->wq = WR(q); } return (sscop); } static void sscop_free_priv(queue_t * q) { sscop_t *m2 = SSCOP_PRIV(q); kmem_cache_free(sscop_cachep, sscop); return; } static void sscop_init_priv(void) { if (!(sscop_cachep = kmem_find_general_cachep(sizeof(sscop_t)))) { /* allocate a new cachep */ } return; } /* * ------------------------------------------------------------------------- * * OPEN - Each open * * ------------------------------------------------------------------------- */ static int sscop_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp) { (void) crp; if (q->q_ptr != NULL) return (0); /* * FIXME: we have to open this as a driver, not a module */ if (sflag == MODOPEN || WR(q)->q_next != NULL) { /* * FIXME: check to make sure that the module we are being * pushed over is compatible (i.e. it is the right kind of * transport module. */ if (!(sscop = kmem_cache_alloc(sscop_priv_cachep, SLAB_HWCACHE_ALIGN))) return ENOMEM; RD(q)->q_ptr = WR(q)->q_ptr = sscop; sscop->rq = RD(q); sscop->wq = WR(q); /* * TODO: more structure initialization */ return (0); } return EIO; } /* * ------------------------------------------------------------------------- * * CLOSE - Last close * * ------------------------------------------------------------------------- */ static int sscop_close(queue_t * q, int flag, cred_t * crp) { sscop_t *sscop = SSCOP_PRIV(q); (void) flag; (void) crp; /* * TODO: make sure everything is deallocated */ kmem_cache_free(sscop_priv_cachep, sscop); return (0); } /* * ========================================================================= * * LiS MODULE INITIALIZATION * * ========================================================================= */ static int sscop_initialized = 0; #ifndef LIS_REGISTERED static inline void sscop_init(void) #else __initfunc(void sscop_init(void)) #endif { if (sscop_initialized) return; sscop_initialized = 1; printk(KERN_INFO SSCOP_BANNER); /* console splash */ #ifndef LIS_REGISTERED if (!(sscop_minfo.mi_idnum = lis_register_strmod(&sscop_info, sscop_minfo.mi_idname))) { cmn_err(CE_NOTE, "sscop: couldn't register as module\n"); sscop_minfo.mi_idnum = 0; } #endif } #ifndef LIS_REGISTERED static inline void sscop_terminate(void) #else __initfunc(void sscop_terminate(void)) #endif { if (!sscop_initialized) return; sscop_initialized = 0; #ifndef LIS_REGISTERED if (sscop_minfo.mi_idnum) if ((sdt_minfo.mi_idnum = lis_unregister_strmod(&sscop_info))) { cmn_err(CE_WARN, "sdt: couldn't unregister as module!\n"); } #endif }; /* * ========================================================================= * * LINUX KERNEL MODULE INITIALIZATION * * ========================================================================= */ #ifdef MODULE int init_module(void) { sscop_init(); return (0); } void cleanup_module(void) { sscop_terminate(); return; } #endif
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |