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_t_user.c#ident "@(#) $RCSfile: sscop_t_user.c,v $ $Name: $($Revision: 0.8.2.1 $) $Date: 2002/10/18 02:37:53 $" static char const ident[] = "$RCSfile: sscop_t_user.c,v $ $Name: $($Revision: 0.8.2.1 $) $Date: 2002/10/18 02:37:53 $"; /* * ========================================================================= * * T-User --> T-Provider (SSCOP) Primitives (M_CTL, M_PROTO, M_PCPROTO) * * ========================================================================= */ /* * T_INFO_REQ * ------------------------------------------------------------------------- */ static int t_info_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; (void) pdu; if (!(mp = t_info_ack(q))) return (-ENOBUFS); freemsg(pdu); qreply(q, mp); return (0); } /* * T_ADDR_REQ * ------------------------------------------------------------------------- */ static int t_addr_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; (void) pdu; if (!(mp = t_addr_ack(q))) return (-ENOBUFS); freemsg(pdu); qreply(q, mp); return (0); } /* * T_OPTMGMT_REQ * ------------------------------------------------------------------------- */ static int t_optmgmt_req(queue_t * q, mblk_t * pdu) { int err; mblk_t *mp; sscop_t *sp = SSCOP_PRIV(q); if (sp->t_state != TS_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; switch (flags) { case T_NEGOTIATE: { caddr_t req = opt_ptr; struct t_opthdr *reqoh; for (req = opt_ptr; req <= opt_ptr + opt_len - sizeof(*reqoh); req = req + reqoh->len) { reqoh = (struct t_opthdr *) req; switch (reqoh->level) { case T_SSCOP: switch (reqoh->name) { case T_ALLOPT: } } } } case T_CHECK: { caddr_t req = opt_ptr; struct t_opthdr *reqoh; for (req = opt_ptr; req <= opt_ptr + opt_len - sizeof(*reqoh); req = req + reqoh->len) { reqoh = (struct t_opthdr *) req; switch (reqoh->level) { case T_SSCOP: switch (reqoh->name) { } } } } case T_DEFAULT: { caddr_t req = opt_ptr; struct t_opthdr *reqoh; for (req = opt_ptr; req <= opt_ptr + opt_len - sizeof(*reqoh); req = req + reqoh->len) { reqoh = (struct t_opthdr *) req; switch (reqoh->level) { case T_SSCOP: switch (reqoh->name) { case T_ALLOPT: } default: } } } case T_CURRENT: { caddr_t req = opt_ptr; struct t_opthdr *reqoh; for (req = opt_ptr; req <= opt_ptr + opt_len - sizeof(*reqoh); req = req + reqoh->len) { reqoh = (struct t_opthdr *) req; switch (reqoh->level) { case T_SSCOP: switch (reqoh->name) { case T_ALLOPT: } } } } } } optmgmt_req_outstate: err = TOUTSTATE; goto optmgmt_req_error; optmgmt_req_error: freemsg(pdu); qreply(q, t_error_ack(T_OPTMGMT_REQ, err)); return (0); } /* * T_BIND_REQ * ------------------------------------------------------------------------- */ static int t_bind_req(queue_t * q, mblk_t * pdu) { mblk_t *mp; sscop_t *sp = SSCOP_PRIV(q); switch (sp->t_state) { case TS_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_cache_alloc(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->t_state = TS_IDLE; freemsg(pdu); mp = t_bind_ack(q); qreply(q, mp); return (0); } } t_bind_req_outstate: err = TOUTSTATE; goto t_bind_req_error; t_bind_req_badaddr: err = TBADADDR; goto t_bind_req_error; t_bind_req_addrbusy: err = TADDRBUSY; goto t_bind_req_error; t_bind_req_noaddr: err = TNOADDR; 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: sscop_free_baddrs(sp); freemsg(pdu); qreply(q, t_error_ack(T_BIND_REQ, err)); return (0); } /* * T_UNBIND_REQ * ------------------------------------------------------------------------- */ 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->t_state != TS_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); } } sscop_free_baddrs(sp); sp->t_state = TS_UNBND; freemsg(pdu); mp = t_ok_ack(T_UNBIND_REQ); qreply(q, mp); return (0); t_unbind_req_outstate: err = TOUTSTATE; goto t_unbind_req_error; t_unbind_req_error: freemsg(pdu); qreply(q, t_error_ack(T_UNBIND_REQ, err)); return (0); } /* * T_CONN_REQ * ------------------------------------------------------------------------- */ 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->t_state != TS_IDLE) goto t_conn_req_outstate; /* pull addresses out of the destination */ if (dst_len < sizeof(u16) + sizeof(u32) || dst_len > mp->b_wptr - dst_ptr) goto t_conn_req_badaddr; 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)) { if (!((daddr = *((u32 *) dst_ptr)++) & 0xff000000)) goto t_conn_req_badaddr; sdp_daddr_include(sp, daddr); } /* * 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; /* FIXME: these should be left at set values, set them this way when the sscop_t is intialized. */ // 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); { caddr_t op; struct t_opthdr *oh; const caddr_t end_ptr = opt_ptr + opt_len - sizeof(*oh); u16 *sidp = NULL; u32 *ppip = NULL; u16 *istp = NULL; u16 *ostp = NULL; for (op = opt_ptr, oh = (struct t_opthdr *) op; op <= end_ptr; op += PADC(oh->len), oh = (struct t_opthdr *) op) { if (oh->level == T_INET_SSCOP) { switch (oh->name) { case T_ALLOPT: break; case SSCOP_I_STREAMS: if (oh->len != sizeof(*oh) + sizeof(*istp)) goto t_conn_req_badopt; if (!*(istp = ((u16 *) oh->value))) goto t_conn_req_badopt; continue; case SSCOP_O_STREAMS: if (oh->len != sizeof(*oh) + sizeof(*ostp)) goto t_conn_req_badopt; if (!*(ostp = ((u16 *) oh->value))) goto t_conn_req_badopt; continue; case SSCOP_SID: if (oh->len != sizeof(*oh) + sizeof(sid)) goto t_conn_req_badopt; if (*(sidp = ((u16 *) oh->value)) >= sp->n_ostr) goto t_conn_req_badopt; continue; case SSCOP_PPI: if (oh->len != sizeof(*oh) + sizeof(ppi)) goto t_conn_req_badopt; ppip = ((u32 *) oh->value); default: continue; } break; } } if (sidp) sp->sid = *sidp; if (ppip) sp->ppi = *ppip; if (istp) sp->n_istr = *istp; if (ostp) sp->n_ostr = *ostp; } /* * 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->t_state = T_WCON_CREQ; freemsg(pdu); qreply(q, t_ok_ack(T_CONN_REQ)); return (0); /* Try to connect to the first address */ t_conn_req_outstate: err = TOUTSTATE; goto t_conn_req_error; t_conn_req_badopt: err = TBADOPT; goto t_conn_req_error; t_conn_req_badaddr: err = TBADADDR; goto t_conn_req_error; t_conn_req_error: bzero(&sp->l, sizeof(sp->l)); sscop_free_daddrs(sp); freemsg(pdu); qreply(q, t_error_ack(T_CONN_REQ, err)); return (0); } /* * T_CONN_RES * ------------------------------------------------------------------------- */ 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->t_state) { case TS_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 = bufq_peek(&sp->connect_queue); 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->t_state == TS_UNBND) goto conn_res_badaddr; if (aq->t_state != TS_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; u16 *istp = NULL; u16 *ostp = 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->len), oh = (struct t_opthdr *) op) { if (oh->level == T_INET_SSCOP) { switch (oh->name) { case SSCOP_SID: if (oh->len != sizeof(*oh) + sizeof(*sidp)) goto conn_res_badopt; sidp = ((u16 *) oh->value); continue; case SSCOP_PPI: if (oh->len != sizeof(*oh) + sizeof(*ppip)) goto conn_res_badopt; ppip = ((u32 *) oh->value); continue; case SSCOP_I_STREAMS: if (oh->len != sizeof(*oh) + sizeof(*istp)) goto conn_res_badopt; istp = ((u16 *) oh->value); continue; case SSCOP_O_STREAMS: if (oh->len != sizeof(*oh) + sizeof(*ostp)) goto conn_res_badopt; ostp = ((u16 *) 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->t_state = TS_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; sscop_free_strms(sp); sscop_free_daddrs(sp); sscop_free_saddrs(sp); { 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 (!sscop_daddr_include(ap, *addp++)) goto conn_res_nomem; for (i = 0; i < ck->sta_num; i++, ssp = (*ssp)->next) { if (! (*ssp = kmem_cache_alloc(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 = TOUTSTATE; goto conn_res_error; conn_res_nomem: err = -ENOMEM; goto conn_res_free_error; conn_res_badaddr: err = TBADADDR; goto conn_res_error; conn_res_resaddr: err = TRESADDR; goto conn_res_error; conn_res_badseq: err = TBADSEQ; goto conn_res_error; conn_res_badf: err = TBADF; goto conn_res_error; conn_res_badopt: err = TBADOPT; 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: sscop_free_daddrs(sp); sscop_free_saddrs(sp); conn_res_error: freemsg(pdu); qreply(q, t_error_ack(T_CONN_RES, TOUTSTATE)); return (0); } /* * T_DATA_REQ * ------------------------------------------------------------------------- */ 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->t_state) { case TS_DATA_XFER: case TS_WREQ_ORDREL: /* SHUTDOWN-RECEIVED */ 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 TS_IDLE: freemsg(pdu); return (0); } data_req_error: freemsg(pdu); qreply(q, t_m_error(EPROTO)); return (0); } /* * T_EXDATA_REQ * ------------------------------------------------------------------------- */ 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->t_state) { case TS_DATA_XFER: case TS_WREQ_ORDREL: /* SHUTDOWN-RECEIVED */ 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 TS_IDLE: freemsg(pdu); return (0); } freemsg(pdu); qreply(q, t_m_error(EPROTO)); return (0); } /* * T_OPTDATA_REQ * ------------------------------------------------------------------------- */ 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->t_state) { case TS_DATA_XFER: case TS_WREQ_ORDREL: /* SHUTDOWN-RECEIVED */ { uint flags = 0; struct sscop_strm *st = sp->ostr; uint *morep; uint *sppip; { u16 *sidp = &sp->sid; u32 *ppip = &sp->ppi; 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->len), oh = (struct t_opthdr *) op) { if (oh->level == T_INET_SSCOP) { switch (oh->name) { case SSCOP_SID: if (oh->len == sizeof(*oh) + sizeof(*sidp)) sidp = ((u16 *) oh->value); continue; case SSCOP_PPI: if (oh->len == sizeof(*oh) + sizeof(*ppip)) ppip = ((u32 *) oh->value); continue; } } } if (!(st = sscop_find_ostr(sp, *sidp)) && !(st = sscop_alloc_ostr(sp, *sidp))) return -ENOMEM; } if (flag & T_EXPEDITED) { flags |= SSCOPCB_FLAG_URG; morep = &st->x.more; st->x.ppi = *ppip; } else { flags &= ~SSCOPCB_FLAG_URG; morep = &st->n.more; st->n.ppi = *ppip; } 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 TS_IDLE: freemsg(pdu); return (0); } optdata_req_error: freemsg(pdu); qreply(q, t_m_error(EPROTO)); return (0); } /* * T_UNITDATA_REQ * ------------------------------------------------------------------------- */ static int t_unitdata_req(queue_t * q, mblk_t * pdu) { freemsg(pdu); qreply(q, t_error_ack(T_UNIDATA_REQ, TNOTSUPPORT)); return (0); } /* * T_DISCON_REQ * ------------------------------------------------------------------------- */ 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->t_state == TS_WRES_CIND) { mblk_t *mp; for (mp = bufq_peek(&sp->connect_queue); 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-> t_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 */ sscop_free_daddrs(sp); /* remove local addresses */ sscop_free_saddrs(sp); /* 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->t_state = T_IDLE; freemsg(pdu); qreply(q, t_ok_ack(T_DISCON_REQ)); return (0); } discon_req_outstate: err = TOUTSTATE; goto discon_req_error; discon_req_badseq: err = TBADSEQ; goto discon_req_error; discon_req_error: freemsg(pdu); qreply(q, t_error_ack(T_DISCON_REQ, err)); return (0); } /* * T_ORDREL_REQ * ------------------------------------------------------------------------- */ 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->t_state) { case TS_DATA_XFER: freemsg(pdu); if (!bufq_size(&sp->write_queue) && !bufq_size(&sp->retrans_queue)) sscop_send_shutdown(sp); sp->t_state = TS_WIND_ORDREL; return (0); case TS_WREQ_ORDREL: /* SHUTDOWN-RECEIVED */ freemsg(pdu); if (!bufq_size(&sp->retrans_queue)) sscop_send_shutdown_ack(sp); sp->t_state = TS_IDLE; return (0); } sp->t_state = TS_NO_STATES; 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 */ }; static __inline__ int sscop_t_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); } int sscop_w_proto(queue_t * q, mblk_t * mp) { return sscop_t_proto(q, mp); } int sscop_w_pcproto(queue_t * q, mblk_t * mp) { return sscop_t_proto(q, mp); }
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |