/* * Copyright (c) 1996-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Author: Hartmut Brandt * * $Begemot: libunimsg/netnatm/sig/sig_call.c,v 1.65 2004/08/05 07:11:00 brandt Exp $ * * Call instance handling * * Note: * In all functions that handle messages from the user or from * the SAAL, commit memory allocation always at the begin of the * function. If allocation fails, ignore saal messages and * respond with an error to user messages. */ #include #include #include #include #include #include #include #include static enum call_state state_compat(struct call *, enum uni_callstate); static void respond_drop_party_ack(struct call *, struct uni_ie_epref *, u_int); #define DEF_PRIV_SIG(NAME, FROM) [SIG##NAME] = "SIG"#NAME, static const char *const call_sigs[] = { DEF_CALL_SIGS }; #undef DEF_PRIV_SIG TIMER_FUNC_CALL(t308, t308_func) TIMER_FUNC_CALL(t303, t303_func) TIMER_FUNC_CALL(t301, t301_func) TIMER_FUNC_CALL(t310, t310_func) TIMER_FUNC_CALL(t313, t313_func) TIMER_FUNC_CALL(t322, t322_func) const struct callstates callstates[] = { [CALLST_NULL] = { "NU0", UNI_CALLSTATE_U0 }, [CALLST_U1] = { "U1", UNI_CALLSTATE_U1 }, [CALLST_U3] = { "U3", UNI_CALLSTATE_U3 }, [CALLST_U4] = { "U4", UNI_CALLSTATE_U4 }, [CALLST_U6] = { "U6", UNI_CALLSTATE_U6 }, [CALLST_U7] = { "U7", UNI_CALLSTATE_U7 }, [CALLST_U8] = { "U8", UNI_CALLSTATE_U8 }, [CALLST_U9] = { "U9", UNI_CALLSTATE_U9 }, [CALLST_U10] = { "U10", UNI_CALLSTATE_U10 }, [CALLST_U11] = { "U11", UNI_CALLSTATE_U11 }, [CALLST_U12] = { "U12", UNI_CALLSTATE_U12 }, [CALLST_N1] = { "N1", UNI_CALLSTATE_N1 }, [CALLST_N3] = { "N3", UNI_CALLSTATE_N3 }, [CALLST_N4] = { "N4", UNI_CALLSTATE_N4 }, [CALLST_N6] = { "N6", UNI_CALLSTATE_N6 }, [CALLST_N7] = { "N7", UNI_CALLSTATE_N7 }, [CALLST_N8] = { "N8", UNI_CALLSTATE_N8 }, [CALLST_N9] = { "N9", UNI_CALLSTATE_N9 }, [CALLST_N10] = { "N10", UNI_CALLSTATE_N10 }, [CALLST_N11] = { "N11", UNI_CALLSTATE_N11 }, [CALLST_N12] = { "N12", UNI_CALLSTATE_N12 }, }; static void unx_send_add_party_rej(struct call *c, struct uni_all *u); static __inline void set_call_state(struct call *c, enum call_state state) { ASSERT(state == CALLST_NULL || (c->uni->proto == UNIPROTO_UNI40U && (state >= CALLST_U1 && state <= CALLST_U12)) || (c->uni->proto == UNIPROTO_UNI40N && (state >= CALLST_N1 && state <= CALLST_N12)), ("setting wrong callstate for proto %u: %u", c->uni->proto, state)); if (c->cstate != state) { VERBOSE(c->uni, UNI_FAC_CALL, 1, "call %d/%d %s -> %s", c->cref, c->mine, callstates[c->cstate].name, callstates[state].name); c->cstate = state; } } static enum uni_callstate map_callstate(enum call_state state) { return (callstates[state].ext); } /* * Find the call. Assume, that the cref is one of a message just received. * That is, if the call reference flag is 0 it is his call, if it is 1 it * is my call. */ struct call * uni_find_call(struct uni *uni, struct uni_cref *cref) { struct call *c; TAILQ_FOREACH(c, &uni->calls, link) if (c->cref == cref->cref && (!c->mine == !cref->flag)) return (c); return (NULL); } struct call * uni_find_callx(struct uni *uni, u_int cref, u_int mine) { struct call *c; TAILQ_FOREACH(c, &uni->calls, link) if (c->cref == cref && !c->mine == !mine) return (c); return (NULL); } /* * Create a new call instance. The type must be set by the caller. */ struct call * uni_create_call(struct uni *uni, u_int cref, u_int mine, uint32_t cookie) { struct call *c; struct uniapi_call_created *ind; struct uni_msg *api; if ((c = CALL_ALLOC()) == NULL) return (NULL); if ((ind = ALLOC_API(struct uniapi_call_created, api)) == NULL) { CALL_FREE(c); return (NULL); } ind->cref.cref = cref; ind->cref.flag = mine; c->uni = uni; c->type = CALL_NULL; c->cref = cref; c->mine = mine; c->cstate = CALLST_NULL; TAILQ_INIT(&c->parties); TIMER_INIT_CALL(c, t301); TIMER_INIT_CALL(c, t303); TIMER_INIT_CALL(c, t308); TIMER_INIT_CALL(c, t310); TIMER_INIT_CALL(c, t313); TIMER_INIT_CALL(c, t322); TAILQ_INSERT_HEAD(&uni->calls, c, link); uni->funcs->uni_output(uni, uni->arg, UNIAPI_CALL_CREATED, cookie, api); VERBOSE(c->uni, UNI_FAC_CALL, 1, "created call %u/%s", c->cref, c->mine ? "mine" : "his"); return (c); } struct call * uni_create_new_call(struct uni *uni, uint32_t cookie) { struct call *c; uint32_t old = uni->cref_alloc++; again: if (uni->cref_alloc == (1 << 23)) uni->cref_alloc = 1; if (uni->cref_alloc == old) return (NULL); /* all crefs exhausted!!! */ TAILQ_FOREACH(c, &uni->calls, link) if (c->mine && c->cref == uni->cref_alloc) { uni->cref_alloc++; goto again; } return (uni_create_call(uni, uni->cref_alloc, 1, cookie)); } /* * Assume timers are all stopped. Memory is not actually freed unless * the reference count drops to 0. * This function is assumed to remove the call from the parent UNI's * call queue. */ void uni_destroy_call(struct call *c, int really) { struct uniapi_call_destroyed *ind; struct uni_msg *api; struct party *p; VERBOSE(c->uni, UNI_FAC_CALL, 1, "destroying call %u/%s", c->cref, c->mine ? "mine" : "his"); TIMER_DESTROY_CALL(c, t301); TIMER_DESTROY_CALL(c, t303); TIMER_DESTROY_CALL(c, t308); TIMER_DESTROY_CALL(c, t310); TIMER_DESTROY_CALL(c, t313); TIMER_DESTROY_CALL(c, t322); TAILQ_REMOVE(&c->uni->calls, c, link); uni_delsig(c->uni, SIG_CALL, c, NULL); while ((p = TAILQ_FIRST(&c->parties)) != NULL) { TAILQ_REMOVE(&c->parties, p, link); uni_destroy_party(p, really); } if (!really) { ind = ALLOC_API(struct uniapi_call_destroyed, api); if (ind != NULL) { ind->cref.cref = c->cref; ind->cref.flag = c->mine; uni_enq_coord(c->uni, SIGO_CALL_DESTROYED, 0, api); } uni_enq_call(c, SIGC_CALL_DELETE, 0, NULL, NULL); return; } CALL_FREE(c); } static void allocate_epref(struct call *c, struct uni_ie_epref *epref) { struct party *p; uint32_t old = c->epref_alloc++; again: if (c->epref_alloc == (1 << 15)) c->epref_alloc = 0; if (c->epref_alloc == old) return; /* all crefs exhausted!!! */ TAILQ_FOREACH(p, &c->parties, link) if (p->epref == c->epref_alloc) { c->epref_alloc++; goto again; } IE_SETPRESENT(*epref); epref->flag = 0; epref->epref = c->epref_alloc; epref->h.coding = UNI_CODING_ITU; epref->h.act = UNI_IEACT_DEFAULT; } static void reset_all_timers(struct call *c) { TIMER_STOP_CALL(c, t301); TIMER_STOP_CALL(c, t303); TIMER_STOP_CALL(c, t308); TIMER_STOP_CALL(c, t310); TIMER_STOP_CALL(c, t313); TIMER_STOP_CALL(c, t322); } /* * Initiate call clearing because of a problem. This is label D in * the SDLs and is called from many places. * The call must have constructed the cause IE in struct call. * * Q.2971:Call-Control-U 27/39 * Q.2971:Call-Control-N 28/39 * * Memory problems are handled differently here: we simply ignore them * by not sending messages or user indications. Because of T308 we * may be lucky to send the message in a second run. * * It is assumed, that the cause for the release is constructed by * the calling function in uni->cause. */ static void clear_callD(struct call *c) { struct uni_msg *api; struct uniapi_release_indication *ind; struct party *p; struct uni_all *rel; /* * Send indication to API */ if ((ind = ALLOC_API(struct uniapi_release_indication, api)) != NULL) { ind->release.hdr.cref.cref = c->cref; ind->release.hdr.cref.flag = c->mine; ind->release.hdr.act = UNI_MSGACT_DEFAULT; ind->release.cause[0] = c->uni->cause; c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_RELEASE_indication, 0, api); } reset_all_timers(c); if (c->type == CALL_LEAF || c->type == CALL_ROOT) { TAILQ_FOREACH(p, &c->parties, link) { uni_enq_party(p, SIGP_RELEASE_request, 0, NULL, NULL); } } memset(&c->msg_release, 0, sizeof(c->msg_release)); c->msg_release.cause[0] = c->uni->cause; if ((rel = UNI_ALLOC()) != NULL) { rel->u.release = c->msg_release; MK_MSG_ORIG(rel, UNI_RELEASE, c->cref, !c->mine); (void)uni_send_output(rel, c->uni); UNI_FREE(rel); } TIMER_START_CALL(c, t308, c->uni->timer308); c->cnt308 = 0; if (c->uni->proto == UNIPROTO_UNI40N) set_call_state(c, CALLST_N12); else set_call_state(c, CALLST_U11); } /**********************************************************************/ /* * SETUP message in state NULL * * Q.2971:Call-Control-U 4/39 * Q.2971:Call-Control-N 4/39 */ static void un0_setup(struct call *c, struct uni_msg *m, struct uni_all *u, enum call_state new_state) { struct uni_all *resp; struct party *p; struct uniapi_setup_indication *ind; struct uni_msg *api; enum verify v; if ((ind = ALLOC_API(struct uniapi_setup_indication, api)) == NULL) { clear: uni_destroy_call(c, 0); uni_msg_destroy(m); UNI_FREE(u); return; } /* * Analyze message */ (void)uni_decode_body(m, u, &c->uni->cx); MANDATE_IE(c->uni, u->u.setup.bearer, UNI_IE_BEARER); MANDATE_IE(c->uni, u->u.setup.traffic, UNI_IE_TRAFFIC); MANDATE_IE(c->uni, u->u.setup.called, UNI_IE_CALLED); /* * UNI4.0: 9.1.1.2 Notes 2/3 */ if (!IE_ISPRESENT(u->u.setup.qos)) MANDATE_IE(c->uni, u->u.setup.exqos, UNI_IE_EXQOS); if (!IE_ISPRESENT(u->u.setup.exqos)) MANDATE_IE(c->uni, u->u.setup.qos, UNI_IE_QOS); /* * Q.2971 */ if (IE_ISGOOD(u->u.setup.bearer) && u->u.setup.bearer.cfg == UNI_BEARER_MP) { if (IE_ISGOOD(u->u.setup.epref) && u->u.setup.epref.flag == 1) { IE_SETERROR(u->u.setup.epref); UNI_SAVE_IERR(&c->uni->cx, UNI_IE_EPREF, u->u.setup.epref.h.act, UNI_IERR_BAD); } uni_mandate_epref(c->uni, &u->u.setup.epref); } v = uni_verify(c->uni, u->u.hdr.act); switch (v) { case VFY_RAI: uni_respond_status_verify(c->uni, &u->u.hdr.cref, UNI_CALLSTATE_U0, NULL, 0); /* FALLTHRU */ case VFY_I: uni_msg_destroy(api); goto clear; case VFY_RAIM: case VFY_CLR: if ((resp = UNI_ALLOC()) != NULL) { MK_MSG_RESP(resp, UNI_RELEASE_COMPL, &u->u.hdr.cref); uni_vfy_collect_ies(c->uni); resp->u.release_compl.cause[0] = c->uni->cause; uni_send_output(resp, c->uni); UNI_FREE(resp); } uni_msg_destroy(api); goto clear; case VFY_RAP: case VFY_RAPU: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(new_state), NULL, 0); /* FALLTHRU */ case VFY_OK: break; } if (u->u.setup.bearer.cfg == UNI_BEARER_P2P) { c->type = CALL_P2P; } else { c->type = CALL_LEAF; if ((p = uni_create_party(c, &u->u.setup.epref)) == NULL) { uni_msg_destroy(api); goto clear; } uni_enq_party(p, SIGP_SETUP, 0, NULL, NULL); } ind->setup.hdr = u->u.hdr; copy_msg_setup(&u->u.setup, &ind->setup); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_SETUP_indication, 0, api); uni_msg_destroy(m); UNI_FREE(u); set_call_state(c, new_state); } /* * Setup.request from user * * Q.2971:Call-Control-U 4/39 (U0) * Q.2971:Call-Control-N 4/39 (N0) */ static void un0_setup_request(struct call *c, struct uni_msg *m, uint32_t cookie, enum call_state new_state) { struct uniapi_setup_request *arg = uni_msg_rptr(m, struct uniapi_setup_request *); struct uni_setup *setup = &arg->setup; struct uni_all *out; struct party *p; if (!IE_ISGOOD(setup->bearer)) { uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_ERROR_MISSING_IE, cookie); uni_destroy_call(c, 0); return; } if ((out = UNI_ALLOC()) == NULL) { uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_ERROR_NOMEM, cookie); uni_destroy_call(c, 0); return; } c->msg_setup = *setup; if (IE_ISGOOD(setup->connid)) c->connid = setup->connid; if (setup->bearer.cfg == UNI_BEARER_P2P) { c->type = CALL_P2P; } else { c->type = CALL_ROOT; /* * If the user didn't specify a endpoint reference, * use 0. Use IE_IGNORE accoring to Appendix II Q.2971 */ if (!IE_ISPRESENT(c->msg_setup.epref)) { MK_IE_EPREF(c->msg_setup.epref, 0, 0); if (c->uni->proto == UNIPROTO_UNI40N) c->msg_setup.epref.h.act = UNI_IEACT_IGNORE; } else if (!IE_ISGOOD(c->msg_setup.epref)) { uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_ERROR_BAD_IE, cookie); uni_destroy_call(c, 0); return; } if ((p = uni_create_partyx(c, 0, 1, cookie)) == NULL) { uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_ERROR_NOMEM, cookie); uni_destroy_call(c, 0); return; } uni_enq_party(p, SIGP_SETUP_request, cookie, NULL, NULL); } uni_msg_destroy(m); out->u.setup = c->msg_setup; MK_MSG_ORIG(out, UNI_SETUP, c->cref, !c->mine); (void)uni_send_output(out, c->uni); UNI_FREE(out); TIMER_START_CALL(c, t303, c->uni->timer303); c->cnt303 = 0; set_call_state(c, new_state); uniapi_call_error(c, UNIAPI_OK, cookie); } /* * CALL PROCEEDING message * * Q.2971:Call-Control-U 6/39 (in U1) * Q.2971:Call-Control-N 11/39 (in N6) */ static void u1n6_call_proc(struct call *c, struct uni_msg *m, struct uni_all *u, enum call_state new_state) { struct uni_call_proc *cp = &u->u.call_proc; struct uniapi_proceeding_indication *ind; struct uni_msg *api; ind = ALLOC_API(struct uniapi_proceeding_indication, api); if (ind == NULL) { ignore: uni_msg_destroy(m); UNI_FREE(u); return; } /* * Analyze message */ (void)uni_decode_body(m, u, &c->uni->cx); if (!IE_ISPRESENT(c->connid) && !IE_ISGOOD(cp->connid)) uni_mandate_ie(c->uni, UNI_IE_CONNID); /* * Q.2971: L3MU_01_03 requests us to ignore the message if * the EPREF is missing. */ if (c->msg_setup.bearer.cfg == UNI_BEARER_MP && IE_ISPRESENT(c->msg_setup.epref)) { if (!IE_ISPRESENT(cp->epref)) uni_mandate_ie(c->uni, UNI_IE_EPREF); \ else if (IE_ISGOOD(cp->epref) && (cp->epref.flag != 1 || cp->epref.epref != c->msg_setup.epref.epref)) { IE_SETERROR(cp->epref); UNI_SAVE_IERR(&c->uni->cx, UNI_IE_EPREF, cp->epref.h.act, UNI_IERR_BAD); } } switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: uni_vfy_collect_ies(c->uni); clear_callD(c); /* FALLTHRU */ case VFY_I: uni_msg_destroy(api); goto ignore; case VFY_RAIM: case VFY_RAI: report: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), NULL, 0); uni_msg_destroy(api); goto ignore; case VFY_RAP: case VFY_RAPU: if (c->type == CALL_ROOT && !IE_ISGOOD(cp->epref)) goto report; uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(new_state), NULL, 0); /* FALLTHRU */ case VFY_OK: break; } TIMER_STOP_CALL(c, t303); if (IE_ISGOOD(cp->connid)) c->connid = cp->connid; ind->call_proc.hdr = u->u.hdr; copy_msg_call_proc(cp, &ind->call_proc); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_PROCEEDING_indication, 0, api); TIMER_START_CALL(c, t310, c->uni->timer310); uni_msg_destroy(m); UNI_FREE(u); set_call_state(c, new_state); } /* * T303 tick. * * Q.2971:Call-Control-U 6/39 * Q.2971:Call-Control-N 11/39 */ static void u1n6_t303(struct call *c) { struct uni_all *msg; struct uniapi_release_confirm *conf; struct uni_msg *api; VERBOSE(c->uni, UNI_FAC_TIMEOUT, 1, "call %u/%s T303 tick %d", c->cref, c->mine ? "mine" : "his", c->cnt303 + 1); if (++c->cnt303 < c->uni->init303) { if ((msg = UNI_ALLOC()) != NULL) { msg->u.setup = c->msg_setup; MK_MSG_ORIG(msg, UNI_SETUP, c->cref, !c->mine); (void)uni_send_output(msg, c->uni); UNI_FREE(msg); } TIMER_START_CALL(c, t303, c->uni->timer303); return; } /* * Send indication to API */ if ((conf = ALLOC_API(struct uniapi_release_confirm, api)) != NULL) { conf->release.hdr.cref.cref = c->cref; conf->release.hdr.cref.flag = c->mine; conf->release.hdr.act = UNI_MSGACT_DEFAULT; MK_IE_CAUSE(conf->release.cause[0], UNI_CAUSE_LOC_USER, UNI_CAUSE_NO_RESPONSE); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_RELEASE_confirm, 0, api); } /* * send to party (there may be only one) */ if (c->type == CALL_ROOT && !TAILQ_EMPTY(&c->parties)) { uni_enq_party(TAILQ_FIRST(&c->parties), SIGP_RELEASE_confirm, 0, NULL, NULL); } uni_destroy_call(c, 0); } /* * T310 (Call Proceeding) timer tick. * * Q.2971:Call-Control-U 7/39 * Q.2971:Call-Control-N 17/39 */ static void u3n9_t310(struct call *c) { VERBOSE(c->uni, UNI_FAC_TIMEOUT, 1, "call %u/%s T310 tick", c->cref, c->mine ? "mine" : "his"); MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_NO_RESPONSE); clear_callD(c); } /* * T301 (Alerting) timer tick. * * Q.2971:Call-Control-U Missing * Q.2971:Call-Control-N 14/39 */ static void u4n7_t301(struct call *c) { VERBOSE(c->uni, UNI_FAC_TIMEOUT, 1, "call %u/%s T301 tick", c->cref, c->mine ? "mine" : "his"); MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_NO_RESP_ALERT); clear_callD(c); } /* * ALERTING received * * Q.2971:Call-Control-U 37/39 (U1) * Q.2971:Call-Control-U 7/39 (U3) * Q.2971:Call-Control-N 9/39 (N6) * Q.2971:Call-Control-N 17/39 (N9) * * There are two errors in the user side SDL Annex A: * * - the resetted timers are swapped (T310 and T303) * * - for U1 we should go to C12, not C3 to start T301. */ static void unx_alerting(struct call *c, struct uni_msg *m, struct uni_all *u, enum call_state new_state) { struct uni_alerting *al = &u->u.alerting; struct uniapi_alerting_indication *ind; struct uni_msg *api; ind = ALLOC_API(struct uniapi_alerting_indication, api); if (ind == NULL) { ignore: uni_msg_destroy(m); UNI_FREE(u); return; } /* * Analyze message */ (void)uni_decode_body(m, u, &c->uni->cx); if (!IE_ISPRESENT(c->connid) && !IE_ISGOOD(al->connid)) uni_mandate_ie(c->uni, UNI_IE_CONNID); /* * Q.2971: L3MU_01_04 requests us to ignore the message if the * EPREF is missing. */ if (c->msg_setup.bearer.cfg == UNI_BEARER_MP && IE_ISPRESENT(c->msg_setup.epref)) { if (!IE_ISPRESENT(al->epref)) uni_mandate_ie(c->uni, UNI_IE_EPREF); \ else if (IE_ISGOOD(al->epref) && (al->epref.flag != 1 || al->epref.epref != c->msg_setup.epref.epref)) { IE_SETERROR(al->epref); UNI_SAVE_IERR(&c->uni->cx, UNI_IE_EPREF, al->epref.h.act, UNI_IERR_BAD); } } switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: uni_vfy_collect_ies(c->uni); clear_callD(c); case VFY_I: uni_msg_destroy(api); goto ignore; case VFY_RAIM: case VFY_RAI: report: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), NULL, 0); uni_msg_destroy(api); goto ignore; case VFY_RAP: case VFY_RAPU: if (c->type == CALL_ROOT && !IE_ISGOOD(al->epref)) goto report; uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), NULL, 0); case VFY_OK: break; } if (c->cstate == CALLST_U1 || c->cstate == CALLST_N6) TIMER_STOP_CALL(c, t303); else if (c->cstate == CALLST_U3 || c->cstate == CALLST_N9) TIMER_STOP_CALL(c, t310); if (IE_ISGOOD(al->connid)) c->connid = al->connid; ind->alerting.hdr = u->u.hdr; copy_msg_alerting(al, &ind->alerting); if (c->type == CALL_LEAF || c->type == CALL_ROOT) { uni_enq_party(TAILQ_FIRST(&c->parties), SIGP_ALERTING, 0, NULL, NULL); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_ALERTING_indication, 0, api); } else { c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_ALERTING_indication, 0, api); TIMER_START_CALL(c, t301, c->uni->timer301); } UNI_FREE(u); uni_msg_destroy(m); set_call_state(c, new_state); } /* * Proceeding.request from API * * Q.2971:Call-Control-U 12/39 (U6) * Q.2971:Call-Control-N 6/39 (N1) */ static void u6n1_proceeding_request(struct call *c, struct uni_msg *m, uint32_t cookie, enum call_state new_state) { struct uni_all *msg; struct uniapi_proceeding_request *arg = uni_msg_rptr(m, struct uniapi_proceeding_request *); if ((msg = UNI_ALLOC()) == NULL) { uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_ERROR_NOMEM, cookie); return; } if (IE_ISGOOD(arg->call_proc.connid)) c->connid = arg->call_proc.connid; msg->u.call_proc = arg->call_proc; MK_MSG_ORIG(msg, UNI_CALL_PROC, c->cref, !c->mine); (void)uni_send_output(msg, c->uni); UNI_FREE(msg); set_call_state(c, new_state); uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_OK, cookie); } /* * Alerting.request from API * * Q.2971:Call-Control-U 13/39 (U6) * Q.2971:Call-Control-U 17/39 (U9) * Q.2971:Call-Control-N 38/39 (N1) * Q.2971:Call-Control-N 7/39 (N3) */ static void unx_alerting_request(struct call *c, struct uni_msg *m, uint32_t cookie, enum call_state new_state) { struct uni_all *msg; struct uniapi_alerting_request *arg = uni_msg_rptr(m, struct uniapi_alerting_request *); if ((msg = UNI_ALLOC()) == NULL) { uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_ERROR_NOMEM, cookie); return; } if (c->type == CALL_ROOT || c->type == CALL_LEAF) { uni_enq_party(TAILQ_FIRST(&c->parties), SIGP_ALERTING_request, cookie, NULL, NULL); } /* * It's not really clear, what happens, if we send another * connid in CALL_PROC and ALERTING */ if (!IE_ISGOOD(c->connid) && IE_ISGOOD(arg->alerting.connid)) c->connid = arg->alerting.connid; msg->u.alerting = arg->alerting; MK_MSG_ORIG(msg, UNI_ALERTING, c->cref, !c->mine); (void)uni_send_output(msg, c->uni); UNI_FREE(msg); set_call_state(c, new_state); uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_OK, cookie); } /* * Setup.response from API * * Q.2971:Call-Control-U 13/39 (U6) * Q.2971:Call-Control-U 14/39 (U7) * Q.2971:Call-Control-U 17/39 (U9) * Q.2971:Call-Control-N 39/39 (N1) * Q.2971:Call-Control-N 7/39 (N3) * Q.2971:Call-Control-N 8/39 (N4) */ static void unx_setup_response(struct call *c, struct uni_msg *m, uint32_t cookie, enum call_state new_state) { struct uni_all *msg; struct uniapi_setup_response *arg = uni_msg_rptr(m, struct uniapi_setup_response *); struct party *p; if ((msg = UNI_ALLOC()) == NULL) { uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_ERROR_NOMEM, cookie); return; } if (!IE_ISGOOD(c->connid) && IE_ISGOOD(arg->connect.connid)) c->connid = arg->connect.connid; if (IE_ISGOOD(arg->connect.epref)) { p = uni_find_partyx(c, arg->connect.epref.epref, !arg->connect.epref.flag); if (p == NULL) { uniapi_call_error(c, UNIAPI_ERROR_BAD_PARTY, cookie); UNI_FREE(msg); uni_msg_destroy(m); return; } /* we need to remember that we have sent the CONNECT from this * party because the CONNECT ACK must move only this party * into P7 */ p->flags |= PARTY_CONNECT; } else if (c->type == CALL_LEAF) { /* XXX don't mandate if only one party */ uniapi_call_error(c, UNIAPI_ERROR_BAD_PARTY, cookie); UNI_FREE(msg); uni_msg_destroy(m); return; } /* inform the parties on the network side */ if (c->uni->proto == UNIPROTO_UNI40N && c->type == CALL_LEAF) TAILQ_FOREACH(p, &c->parties, link) uni_enq_party(p, SIGP_SETUP_response, 0, NULL, NULL); msg->u.connect = arg->connect; MK_MSG_ORIG(msg, UNI_CONNECT, c->cref, !c->mine); (void)uni_send_output(msg, c->uni); UNI_FREE(msg); if (c->uni->proto == UNIPROTO_UNI40U) TIMER_START_CALL(c, t313, c->uni->timer313); set_call_state(c, new_state); uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_OK, cookie); } /* * Setup_complete.request * * Q.2971:Call-Control-N 15/39 (N8) */ static void n8_setup_compl_request(struct call *c, struct uni_msg *m, uint32_t cookie, enum call_state new_state) { struct uni_all *msg; struct uniapi_setup_complete_request *arg = uni_msg_rptr(m, struct uniapi_setup_complete_request *); struct party *p; if ((msg = UNI_ALLOC()) == NULL) { uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_ERROR_NOMEM, cookie); return; } /* inform the parties on the network side */ if (c->uni->proto == UNIPROTO_UNI40N && (c->type == CALL_LEAF || c->type == CALL_ROOT)) { TAILQ_FOREACH(p, &c->parties, link) uni_enq_party(p, SIGP_SETUP_COMPL_request, 0, NULL, NULL); } msg->u.connect_ack = arg->connect_ack; MK_MSG_ORIG(msg, UNI_CONNECT_ACK, c->cref, !c->mine); (void)uni_send_output(msg, c->uni); UNI_FREE(msg); set_call_state(c, new_state); uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_OK, cookie); } /* * CONNECT message * * Q.2971:Call-Control-U 7-8/39 (U3) * Q.2971:Call-Control-U 11/39 (U4) * Q.2971:Call-Control-U 37/39 (U1) * Q.2971:Call-Control-N 9-10/39 (N6) * Q.2971:Call-Control-N 14/39 (N7) * Q.2971:Call-Control-N 17/39 (N9) */ static void unx_connect(struct call *c, struct uni_msg *m, struct uni_all *u, enum call_state new_state) { struct uni_connect *co = &u->u.connect; struct uniapi_setup_confirm *conf; struct uni_msg *api; struct uni_all *ack; struct party *p; conf = ALLOC_API(struct uniapi_setup_confirm, api); if (conf == NULL) { ignore: UNI_FREE(u); uni_msg_destroy(m); return; } if ((ack = UNI_ALLOC()) == NULL) { uni_msg_destroy(api); goto ignore; } /* * Analyze message */ (void)uni_decode_body(m, u, &c->uni->cx); if (!IE_ISPRESENT(c->connid) && !IE_ISGOOD(co->connid)) uni_mandate_ie(c->uni, UNI_IE_CONNID); /* * Q.2971: L3MU_01_05 requires the epref to be present. */ p = NULL; if (c->msg_setup.bearer.cfg == UNI_BEARER_MP) { if (IE_ISPRESENT(c->msg_setup.epref)) { if (!IE_ISPRESENT(co->epref)) uni_mandate_ie(c->uni, UNI_IE_EPREF); \ if (IE_ISGOOD(co->epref) && co->epref.flag != 1) { IE_SETERROR(co->epref); UNI_SAVE_IERR(&c->uni->cx, UNI_IE_EPREF, co->epref.h.act, UNI_IERR_BAD); } } if (IE_ISGOOD(co->epref)) { p = uni_find_party(c, &co->epref); if (p == NULL) { respond_drop_party_ack(c, &co->epref, UNI_CAUSE_ENDP_INV); uni_msg_destroy(api); UNI_FREE(ack); goto ignore; } } } switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: uni_vfy_collect_ies(c->uni); clear_callD(c); /* FALLTHRU */ case VFY_I: uni_msg_destroy(api); UNI_FREE(ack); goto ignore; case VFY_RAIM: case VFY_RAI: report: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), NULL, 0); uni_msg_destroy(api); UNI_FREE(ack); goto ignore; case VFY_RAP: case VFY_RAPU: if (c->type == CALL_ROOT && !IE_ISGOOD(co->epref)) goto report; uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(new_state), NULL, 0); /* FALLTHRU */ case VFY_OK: break; } if (IE_ISGOOD(co->connid)) c->connid = co->connid; if (c->cstate == CALLST_U1 || c->cstate == CALLST_N6) TIMER_STOP_CALL(c, t303); else if (c->cstate == CALLST_U3 || c->cstate == CALLST_N9) TIMER_STOP_CALL(c, t310); else if (c->cstate == CALLST_U4 || c->cstate == CALLST_N7) { if(c->type == CALL_P2P) TIMER_STOP_CALL(c, t301); } /* * This is sent to the party only on the user side and only * to the one party in the epref (L3MU_05_03). */ if (c->uni->proto == UNIPROTO_UNI40U && (c->type == CALL_LEAF || c->type == CALL_ROOT)) uni_enq_party(p, SIGP_CONNECT, 0, NULL, NULL); conf->connect.hdr = u->u.hdr; copy_msg_connect(co, &conf->connect); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_SETUP_confirm, 0, api); if (c->uni->proto == UNIPROTO_UNI40U) { /* this is left to the application on the network side */ MK_MSG_ORIG(ack, UNI_CONNECT_ACK, c->cref, !c->mine); (void)uni_send_output(ack, c->uni); UNI_FREE(ack); } UNI_FREE(u); uni_msg_destroy(m); set_call_state(c, new_state); } /* * T313 (Connect) timer tick. * * Q.2971:Call-Control-U 15/39 */ static void u8_t313(struct call *c) { VERBOSE(c->uni, UNI_FAC_TIMEOUT, 1, "call %u/%s T313 tick", c->cref, c->mine ? "mine" : "his"); MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_RECOVER); ADD_CAUSE_TIMER(c->uni->cause, "313"); clear_callD(c); } /* * CONNECT ACKNOWLEDGE message in U8 * * Q.2971:Call-Control-U 15-16/39 */ static void u8_connect_ack(struct call *c, struct uni_msg *m, struct uni_all *u, enum call_state new_state) { struct uniapi_setup_complete_indication *ind; struct uni_msg *api; ind = ALLOC_API(struct uniapi_setup_complete_indication, api); if (ind == NULL) { ignore: uni_msg_destroy(m); UNI_FREE(u); return; } /* * Analyze message */ (void)uni_decode_body(m, u, &c->uni->cx); switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: uni_vfy_collect_ies(c->uni); clear_callD(c); /* FALLTHRU */ case VFY_I: uni_msg_destroy(api); goto ignore; case VFY_RAIM: case VFY_RAI: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), NULL, 0); uni_msg_destroy(api); goto ignore; case VFY_RAP: case VFY_RAPU: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(new_state), NULL, 0); /* FALLTHRU */ case VFY_OK: break; } TIMER_STOP_CALL(c, t313); if (c->type == CALL_LEAF) { struct party *p; TAILQ_FOREACH(p, &c->parties, link) { if (p->flags & PARTY_CONNECT) { uni_enq_party(p, SIGP_CONNECT_ACK, 0, NULL, NULL); break; } } } ind->connect_ack.hdr = u->u.hdr; copy_msg_connect_ack(&u->u.connect_ack, &ind->connect_ack); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_SETUP_COMPLETE_indication, 0, api); UNI_FREE(u); uni_msg_destroy(m); set_call_state(c, new_state); } /* * CONNECT ACKNOWLEDGE message in N10 * * Q.2971:Call-Control-N 18/39 */ static void n10_connect_ack(struct call *c, struct uni_msg *m, struct uni_all *u) { /* * Analyze message */ (void)uni_decode_body(m, u, &c->uni->cx); switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: uni_vfy_collect_ies(c->uni); clear_callD(c); /* FALLTHRU */ case VFY_I: uni_msg_destroy(m); UNI_FREE(u); return; case VFY_RAIM: case VFY_RAI: case VFY_RAP: case VFY_RAPU: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), NULL, 0); /* FALLTHRU */ case VFY_OK: uni_msg_destroy(m); UNI_FREE(u); return; } } /* * Release.response in U6 or U12. * * Q.2971:Call-Control-U 12/39 (U6) * Q.2971:Call-Control-U 30/39 (U12) * Q.2971:Call-Control-N 6/39 (N1) * Q.2971:Call-Control-N 29/39 (N11) */ static void unx_release_response(struct call *c, struct uni_msg *m, uint32_t cookie) { struct party *p; struct uni_all *msg; struct uniapi_release_response *arg = uni_msg_rptr(m, struct uniapi_release_response *); if ((msg = UNI_ALLOC()) == NULL) { uniapi_call_error(c, UNIAPI_ERROR_NOMEM, cookie); uni_msg_destroy(m); return; } if (c->cstate == CALLST_U6 || c->cstate == CALLST_N1) { if (c->type == CALL_ROOT || c->type == CALL_LEAF) { TAILQ_FOREACH(p, &c->parties, link) uni_enq_party(p, SIGP_RELEASE_response, cookie, NULL, NULL); } } msg->u.release_compl = arg->release_compl; MK_MSG_ORIG(msg, UNI_RELEASE_COMPL, c->cref, !c->mine); (void)uni_send_output(msg, c->uni); UNI_FREE(msg); uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_OK, cookie); uni_destroy_call(c, 0); } /* * Got a RELEASE COMPLETE in any state expect U0 * * Q.2971:Call-Control-U 25/39 * Q.2971:Call-Control-N 26/39 * * This is also called from the restart processes. */ void uni_release_compl(struct call *c, struct uni_all *u) { struct uni_msg *api; struct uniapi_release_confirm *conf; struct party *p; u_int i, j; if ((conf = ALLOC_API(struct uniapi_release_confirm, api)) == NULL) return; reset_all_timers(c); if (c->type == CALL_ROOT || c->type == CALL_LEAF) { TAILQ_FOREACH(p, &c->parties, link) uni_enq_party(p, SIGP_RELEASE_COMPL, 0, NULL, NULL); /* YYY optional call reoffering 10.3.3/10.3.4 */ } conf->release.hdr = u->u.hdr; for (i = j = 0; i < 2; i++) if (IE_ISGOOD(u->u.release_compl.cause[i])) conf->release.cause[j++] = u->u.release_compl.cause[i]; for (i = j = 0; i < UNI_NUM_IE_GIT; i++) if (IE_ISGOOD(u->u.release_compl.git[i])) conf->release.git[j++] = u->u.release_compl.git[i]; if (IE_ISGOOD(u->u.release_compl.uu)) conf->release.uu = u->u.release_compl.uu; if (IE_ISGOOD(u->u.release_compl.crankback)) conf->release.crankback = u->u.release_compl.crankback; c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_RELEASE_confirm, 0, api); uni_destroy_call(c, 0); } static void unx_release_compl(struct call *c, struct uni_msg *m, struct uni_all *u) { (void)uni_decode_body(m, u, &c->uni->cx); (void)uni_verify(c->uni, u->u.hdr.act); /* no point :-) */ uni_release_compl(c, u); uni_msg_destroy(m); UNI_FREE(u); } /* * Got a RELEASE COMPLETE in any state expect U0 and U11 * * Q.2971:Call-Control-U 25/39 * Q.2971:Call-Control-N 26/39 */ static void unx_release(struct call *c, struct uni_msg *m, struct uni_all *u, enum call_state new_state) { struct uniapi_release_indication *ind; struct uni_msg *api; if ((ind = ALLOC_API(struct uniapi_release_indication, api)) == NULL) { uni_msg_destroy(m); UNI_FREE(u); return; } (void)uni_decode_body(m, u, &c->uni->cx); (void)uni_verify(c->uni, u->u.hdr.act); /* no point :-) */ reset_all_timers(c); if (c->type == CALL_ROOT || c->type == CALL_LEAF) { struct party *p; TAILQ_FOREACH(p, &c->parties, link) uni_enq_party(p, SIGP_RELEASE, 0, NULL, NULL); /* YYY optional call reoffering 10.3.3/10.3.4 */ } if (c->cstate != new_state) { /* * According to Q.2971 we should send a 2nd * Release.indication. * According to Q.2931 the recipte of a RELEASE in U12/N11 * is illegal. * According to us make it legal, but don't send a 2nd * indication. */ ind->release.hdr = u->u.hdr; copy_msg_release(&u->u.release, &ind->release); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_RELEASE_indication, 0, api); } else uni_msg_destroy(api); uni_msg_destroy(m); UNI_FREE(u); set_call_state(c, new_state); } /* * Got RELEASE in U11 or N12 * * Q.2971:Call-Control-U 28/39 * Q.2971:Call-Control-N 30/39 */ static void u11n12_release(struct call *c, struct uni_msg *m, struct uni_all *u) { struct uniapi_release_confirm *conf; struct uni_msg *api; if ((conf = ALLOC_API(struct uniapi_release_confirm, api)) == NULL) { uni_msg_destroy(m); UNI_FREE(u); return; } (void)uni_decode_body(m, u, &c->uni->cx); (void)uni_verify(c->uni, u->u.hdr.act); /* no point :-) */ TIMER_STOP_CALL(c, t308); conf->release.hdr = u->u.hdr; copy_msg_release(&u->u.release, &conf->release); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_RELEASE_confirm, 0, api); uni_msg_destroy(m); UNI_FREE(u); uni_destroy_call(c, 0); } /* * NOTIFY message * * Q.2971:Call-Control-U 18/39 * Q.2971:Call-Control-N 19/39 */ static void unx_notify(struct call *c, struct uni_msg *m, struct uni_all *u) { struct uniapi_notify_indication *ind; struct uni_msg *api; struct party *p = NULL; if ((ind = ALLOC_API(struct uniapi_notify_indication, api)) == NULL) { ignore: uni_msg_destroy(m); UNI_FREE(u); return; } /* * Analyze message */ (void)uni_decode_body(m, u, &c->uni->cx); MANDATE_IE(c->uni, u->u.notify.notify, UNI_IE_NOTIFY); if (IE_ISGOOD(u->u.notify.epref)) { if ((p = uni_find_party(c, &u->u.notify.epref)) == NULL) { respond_drop_party_ack(c, &u->u.notify.epref, UNI_CAUSE_ENDP_INV); uni_msg_destroy(api); goto ignore; } } switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: uni_msg_destroy(api); uni_vfy_collect_ies(c->uni); clear_callD(c); goto ignore; case VFY_RAIM: case VFY_RAI: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &u->u.notify.epref, p ? p->state : 0); /* FALLTHRU */ case VFY_I: uni_msg_destroy(api); goto ignore; case VFY_RAP: case VFY_RAPU: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &u->u.notify.epref, p ? p->state : 0); case VFY_OK: /* FALLTHRU */ break; } ind->notify.hdr = u->u.hdr; copy_msg_notify(&u->u.notify, &ind->notify); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_NOTIFY_indication, 0, api); UNI_FREE(u); uni_msg_destroy(m); } /* * Notify.request from user * * Q.2971:Call-Control-U 18/39 * Q.2971:Call-Control-N 19/39 */ static void unx_notify_request(struct call *c, struct uni_msg *m, uint32_t cookie) { struct uni_all *msg; struct uniapi_notify_request *arg = uni_msg_rptr(m, struct uniapi_notify_request *); if ((msg = UNI_ALLOC()) == NULL) { uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_ERROR_NOMEM, cookie); return; } msg->u.notify = arg->notify; MK_MSG_ORIG(msg, UNI_NOTIFY, c->cref, !c->mine); (void)uni_send_output(msg, c->uni); UNI_FREE(msg); uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_OK, cookie); } /**********************************************************************/ /* * Release.request from API in any state except U11, U12, N11, N12 * * Q.2971:Call-Control-U 27/39 * Q.2971:Call-Control-N 28/39 */ static void unx_release_request(struct call *c, struct uni_msg *m, uint32_t cookie, enum call_state new_state) { struct uni_all *msg; struct uniapi_release_request *arg = uni_msg_rptr(m, struct uniapi_release_request *); struct party *p; if ((msg = UNI_ALLOC()) == NULL) { uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_ERROR_NOMEM, cookie); return; } reset_all_timers(c); if (c->type == CALL_LEAF || c->type == CALL_ROOT) { TAILQ_FOREACH(p, &c->parties, link) { uni_enq_party(p, SIGP_RELEASE_request, cookie, NULL, NULL); } } c->msg_release = arg->release; if (!IE_ISPRESENT(c->msg_release.cause[0]) && !IE_ISPRESENT(c->msg_release.cause[1])) MK_IE_CAUSE(c->msg_release.cause[0], UNI_CAUSE_LOC_USER, UNI_CAUSE_UNSPEC); msg->u.release = c->msg_release; MK_MSG_ORIG(msg, UNI_RELEASE, c->cref, !c->mine); (void)uni_send_output(msg, c->uni); UNI_FREE(msg); TIMER_START_CALL(c, t308, c->uni->timer308); c->cnt308 = 0; set_call_state(c, new_state); uni_msg_destroy(m); uniapi_call_error(c, UNIAPI_OK, cookie); } /* * Message with unknown EPREF - send a drop party according to 9.5.3.2.3a) */ static void respond_drop_party_ack(struct call *c, struct uni_ie_epref *epref, u_int cause) { struct uni_all *msg; if ((msg = UNI_ALLOC()) == NULL) return; MK_MSG_ORIG(msg, UNI_DROP_PARTY_ACK, c->cref, !c->mine); MK_IE_EPREF(msg->u.drop_party_ack.epref, epref->epref, !epref->flag); MK_IE_CAUSE(msg->u.drop_party_ack.cause, UNI_CAUSE_LOC_USER, cause); (void)uni_send_output(msg, c->uni); UNI_FREE(msg); } /* * T308 (RELEASE) timer * * Q.2971:Call-Control-U 28/39 * Q.2971:Call-Control-N 30/39 */ static void u11n12_t308(struct call *c) { struct uni_all *msg; struct uni_msg *api; struct uniapi_release_confirm *conf; VERBOSE(c->uni, UNI_FAC_TIMEOUT, 1, "call %u/%s T308 tick %d", c->cref, c->mine ? "mine" : "his", c->cnt308 + 1); if (++c->cnt308 < c->uni->init308) { if ((msg = UNI_ALLOC()) != NULL) { msg->u.release = c->msg_release; MK_MSG_ORIG(msg, UNI_RELEASE, c->cref, !c->mine); if (!IE_ISPRESENT(msg->u.release.cause[1])) { MK_IE_CAUSE(msg->u.release.cause[1], UNI_CAUSE_LOC_USER, UNI_CAUSE_RECOVER); ADD_CAUSE_TIMER(msg->u.release.cause[1], "308"); } (void)uni_send_output(msg, c->uni); UNI_FREE(msg); } TIMER_START_CALL(c, t308, c->uni->timer308); return; } /* * Send indication to API */ if ((conf = ALLOC_API(struct uniapi_release_confirm, api)) != NULL) { conf->release.hdr.cref.cref = c->cref; conf->release.hdr.cref.flag = c->mine; conf->release.hdr.act = UNI_MSGACT_DEFAULT; MK_IE_CAUSE(conf->release.cause[0], UNI_CAUSE_LOC_USER, UNI_CAUSE_RECOVER); ADD_CAUSE_TIMER(conf->release.cause[0], "308"); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_RELEASE_confirm, 0, api); } uni_destroy_call(c, 0); } /**********************************************************************/ /* * STATUS in U11/U12 * * Q.2971:Call-Control-U 29/39 (U11) * Q.2971:Call-Control-U 30/39 (U12) * Q.2971:Call-Control-N 29/39 (N11) * Q.2971:Call-Control-N 31/39 (N12) */ static void un11un12_status(struct call *c, struct uni_msg *m, struct uni_all *u) { enum call_state ns; struct uniapi_release_confirm *conf; struct uni_msg *api; struct party *p; struct uniapi_status_indication *stat; /* * Analyze message */ (void)uni_decode_body(m, u, &c->uni->cx); MANDATE_IE(c->uni, u->u.status.callstate, UNI_IE_CALLSTATE); MANDATE_IE(c->uni, u->u.status.cause, UNI_IE_CAUSE); ns = c->cstate; if (IE_ISGOOD(u->u.status.callstate) && u->u.status.callstate.state == UNI_CALLSTATE_U0) ns = CALLST_NULL; p = NULL; if (IE_ISGOOD(u->u.status.epref)) p = uni_find_party(c, &u->u.status.epref); switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: uni_vfy_collect_ies(c->uni); clear_callD(c); uni_msg_destroy(m); UNI_FREE(u); return; case VFY_RAIM: case VFY_RAI: case VFY_RAP: case VFY_RAPU: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(ns), &u->u.status.epref, p ? p->state : UNI_EPSTATE_NULL); case VFY_I: case VFY_OK: break; } if (ns == c->cstate) { /* * Inform API */ stat = ALLOC_API(struct uniapi_status_indication, api); if (stat != NULL) { stat->cref = u->u.hdr.cref; stat->my_state = map_callstate(c->cstate); stat->his_state = u->u.status.callstate; stat->his_cause = u->u.status.cause; stat->epref = u->u.status.epref; stat->epstate = u->u.status.epstate; stat->my_cause = 0; c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_STATUS_indication, 0, api); } uni_msg_destroy(m); UNI_FREE(u); return; } uni_msg_destroy(m); UNI_FREE(u); /* * Send indication to API */ if ((conf = ALLOC_API(struct uniapi_release_confirm, api)) != NULL) { conf->release.hdr.cref.cref = c->cref; conf->release.hdr.cref.flag = c->mine; conf->release.hdr.act = UNI_MSGACT_DEFAULT; MK_IE_CAUSE(conf->release.cause[0], UNI_CAUSE_LOC_USER, UNI_CAUSE_MSG_INCOMP); ADD_CAUSE_MTYPE(conf->release.cause[0], UNI_STATUS); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_RELEASE_confirm, 0, api); } uni_destroy_call(c, 0); } static int status_enq_filter(struct sig *sig, void *arg) { return (sig->type == SIG_CALL && (struct call *)arg == sig->call && sig->sig == SIGC_SEND_STATUS_ENQ); } /* * STATUS in any state except U0/U11/U12 N0/N11/N12 * * Q.2971:Call-Control-U 32/39 * Q.2971:Call-Control-N 33/39 */ static void unx_status(struct call *c, struct uni_msg *m, struct uni_all *u) { struct uniapi_status_indication *stat; struct uniapi_release_confirm *conf; enum call_state ns; struct uni_msg *api; struct party *p; /* * Analyze message */ (void)uni_decode_body(m, u, &c->uni->cx); MANDATE_IE(c->uni, u->u.status.callstate, UNI_IE_CALLSTATE); MANDATE_IE(c->uni, u->u.status.cause, UNI_IE_CAUSE); ns = c->cstate; if (IE_ISGOOD(u->u.status.callstate)) ns = state_compat(c, u->u.status.callstate.state); p = NULL; if (IE_ISGOOD(u->u.status.epref)) { p = uni_find_party(c, &u->u.status.epref); MANDATE_IE(c->uni, u->u.status.epstate, UNI_IE_EPSTATE); } switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: uni_vfy_collect_ies(c->uni); clear_callD(c); uni_msg_destroy(m); UNI_FREE(u); return; case VFY_RAIM: case VFY_RAI: case VFY_RAP: case VFY_RAPU: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(ns), &u->u.notify.epref, p ? p->state : UNI_EPSTATE_NULL); /* FALLTHRU */ case VFY_I: case VFY_OK: break; } if (u->u.status.callstate.state == UNI_CALLSTATE_U0) { /* release_complete */ uni_msg_destroy(m); UNI_FREE(u); if (c->type == CALL_LEAF || c->type == CALL_ROOT) { TAILQ_FOREACH(p, &c->parties, link) uni_enq_party(p, SIGP_RELEASE_COMPL, 0, NULL, NULL); } /* * Send indication to API */ conf = ALLOC_API(struct uniapi_release_confirm, api); if (conf != NULL) { conf->release.hdr.cref.cref = c->cref; conf->release.hdr.cref.flag = c->mine; conf->release.hdr.act = UNI_MSGACT_DEFAULT; MK_IE_CAUSE(conf->release.cause[0], UNI_CAUSE_LOC_USER, UNI_CAUSE_MSG_INCOMP); ADD_CAUSE_MTYPE(conf->release.cause[0], UNI_STATUS); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_RELEASE_confirm, 0, api); } uni_destroy_call(c, 0); return; } if (IE_ISGOOD(u->u.status.cause) && u->u.status.cause.cause == UNI_CAUSE_STATUS) { c->se_active = 0; TIMER_STOP_CALL(c, t322); uni_undel(c->uni, status_enq_filter, c); } /* * Inform API */ if ((stat = ALLOC_API(struct uniapi_status_indication, api)) != NULL) { stat->cref = u->u.hdr.cref; stat->my_state = map_callstate(c->cstate); stat->his_state = u->u.status.callstate; stat->his_cause = u->u.status.cause; stat->epref = u->u.status.epref; stat->epstate = u->u.status.epstate; } if (ns == c->cstate) { /* compatible or recovered */ if (p != NULL) uni_enq_party(p, SIGP_STATUS, 0, m, u); else { if (IE_ISGOOD(u->u.status.epref) && (!IE_ISGOOD(u->u.status.epstate) || u->u.status.epstate.state != UNI_EPSTATE_NULL)) respond_drop_party_ack(c, &u->u.status.epref, UNI_CAUSE_MSG_INCOMP); uni_msg_destroy(m); UNI_FREE(u); } if (stat != NULL) { stat->my_cause = 0; c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_STATUS_indication, 0, api); } return; } /* incompatible */ if (stat != NULL) { stat->my_cause = UNI_CAUSE_MSG_INCOMP; c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_STATUS_indication, 0, api); } MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_MSG_INCOMP); uni_msg_destroy(m); UNI_FREE(u); clear_callD(c); } /* * Enquiry peer status * * Q.2971:Call-Control-U 31/39 * Q.2971:Call-Control-N 32/39 */ static void unx_status_enquiry_request(struct call *c, struct uni_msg *msg, uint32_t cookie) { struct uniapi_status_enquiry_request *arg = uni_msg_rptr(msg, struct uniapi_status_enquiry_request *); struct party *p; struct uni_all *stat; if (c->se_active) { /* This case is not handled in the SDLs */ uniapi_call_error(c, UNIAPI_ERROR_BUSY, cookie); uni_msg_destroy(msg); return; } if ((c->type == CALL_ROOT || c->type == CALL_LEAF) && IE_ISGOOD(arg->epref)) { if ((p = uni_find_partyx(c, arg->epref.epref, !arg->epref.flag)) == NULL) { uniapi_call_error(c, UNIAPI_ERROR_BAD_PARTY, cookie); uni_msg_destroy(msg); return; } uni_msg_destroy(msg); uni_enq_party(p, SIGP_STATUS_ENQUIRY_request, cookie, NULL, NULL); return; } if ((stat = UNI_ALLOC()) == NULL) { uniapi_call_error(c, UNIAPI_ERROR_NOMEM, cookie); uni_msg_destroy(msg); return; } memset(&c->stat_epref, 0, sizeof(c->stat_epref)); MK_MSG_ORIG(stat, UNI_STATUS_ENQ, c->cref, !c->mine); (void)uni_send_output(stat, c->uni); UNI_FREE(stat); TIMER_START_CALL(c, t322, c->uni->timer322); c->cnt322 = 0; c->se_active = 1; uniapi_call_error(c, UNIAPI_OK, cookie); } /* * T322 tick * * Q.2971:Call-Control-U 34/39 * Q.2971:Call-Control-N 35/39 */ static void unx_t322(struct call *c) { struct uni_all *stat; VERBOSE(c->uni, UNI_FAC_TIMEOUT, 1, "call %u/%s T322 tick %d", c->cref, c->mine ? "mine" : "his", c->cnt322 + 1); if (++c->cnt322 < c->uni->init322) { if ((stat = UNI_ALLOC()) != NULL) { MK_MSG_ORIG(stat, UNI_STATUS_ENQ, c->cref, !c->mine); stat->u.status_enq.epref = c->stat_epref; (void)uni_send_output(stat, c->uni); UNI_FREE(stat); } TIMER_START_CALL(c, t322, c->uni->timer322); return; } c->se_active = 0; MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_RECOVER); ADD_CAUSE_TIMER(c->uni->cause, "322"); clear_callD(c); } /* * STATUS ENQUIRY message * * Q.2971:Call-Control-U 31/39 * Q.2971:Call-Control-N 32/39 */ static void unx_status_enq(struct call *c, struct uni_msg *m, struct uni_all *u) { struct party *p = NULL; u_int epref, flag; /* * Analyze message */ (void)uni_decode_body(m, u, &c->uni->cx); switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: uni_vfy_collect_ies(c->uni); clear_callD(c); uni_msg_destroy(m); UNI_FREE(u); return; case VFY_RAIM: case VFY_RAI: case VFY_RAP: case VFY_RAPU: case VFY_I: case VFY_OK: break; } uni_msg_destroy(m); if ((c->type == CALL_ROOT || c->type == CALL_LEAF) && IE_ISGOOD(u->u.status_enq.epref)) { p = uni_find_party(c, &u->u.status_enq.epref); epref = u->u.status_enq.epref.epref; flag = u->u.status_enq.epref.flag; memset(u, 0, sizeof(*u)); MK_IE_EPREF(u->u.status.epref, epref, !flag); if (p != NULL) MK_IE_EPSTATE(u->u.status.epstate, p->state); else MK_IE_EPSTATE(u->u.status.epstate, UNI_EPSTATE_NULL); } else memset(u, 0, sizeof(*u)); MK_MSG_ORIG(u, UNI_STATUS, c->cref, !c->mine); MK_IE_CALLSTATE(u->u.status.callstate, map_callstate(c->cstate)); MK_IE_CAUSE(u->u.status.cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_STATUS); (void)uni_send_output(u, c->uni); UNI_FREE(u); } /**********************************************************************/ /* * Link-release.indication from SAAL in state U10 or N10. * * Q.2971:Call-Control-U 19/39 * Q.2971:Call-Control-N 20/39 */ static void un10_link_release_indication(struct call *c) { struct party *p; if (c->type == CALL_LEAF || c->type == CALL_ROOT) TAILQ_FOREACH(p, &c->parties, link) { if (p->state != UNI_EPSTATE_ACTIVE) uni_enq_party(p, SIGP_RELEASE_COMPL, 0, NULL, NULL); } uni_enq_coord(c->uni, SIGO_LINK_ESTABLISH_request, 0, NULL); } /* * Link-release.indication from SAAL in all state except U10 and N10. * * Q.2971:Call-Control-U 36/39 * Q.2971:Call-Control-N 37/39 */ static void unx_link_release_indication(struct call *c) { struct uniapi_release_confirm *conf; struct uni_msg *api; struct party *p; if (c->type == CALL_LEAF || c->type == CALL_ROOT) TAILQ_FOREACH(p, &c->parties, link) uni_enq_party(p, SIGP_RELEASE_COMPL, 0, NULL, NULL); if ((conf = ALLOC_API(struct uniapi_release_confirm, api)) != NULL) { conf->release.hdr.cref.cref = c->cref; conf->release.hdr.cref.flag = c->mine; conf->release.hdr.act = UNI_MSGACT_DEFAULT; MK_IE_CAUSE(conf->release.cause[0], UNI_CAUSE_LOC_USER, UNI_CAUSE_DST_OOO); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_RELEASE_confirm, 0, api); } uni_destroy_call(c, 0); } /* * Failed to establish SAAL link. Can happen only in U10 or N10. * * Q.2971:Call-Control-U 19/39 * Q.2971:Call-Control-N 20/39 */ static void un10_link_establish_error_indication(struct call *c) { struct party *p; struct uni_msg *api; struct uniapi_release_confirm *conf; if (c->type == CALL_LEAF || c->type == CALL_ROOT) TAILQ_FOREACH(p, &c->parties, link) uni_enq_party(p, SIGP_RELEASE_COMPL, 0, NULL, NULL); if ((conf = ALLOC_API(struct uniapi_release_confirm, api)) != NULL) { conf->release.hdr.cref.cref = c->cref; conf->release.hdr.cref.flag = c->mine; conf->release.hdr.act = UNI_MSGACT_DEFAULT; MK_IE_CAUSE(conf->release.cause[0], UNI_CAUSE_LOC_USER, UNI_CAUSE_DST_OOO); c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_RELEASE_confirm, 0, api); } uni_destroy_call(c, 0); } /* * Issue a STATUS ENQUIRY of we are not busy * * Q.2971: Call-Control-U: 34/39 * Q.2971: Call-Control-N: 34/39 */ static void call_se(struct call *c) { struct uni_all *stat; c->cnt322 = 0; if (c->se_active) return; memset(&c->stat_epref, 0, sizeof(c->stat_epref)); if ((stat = UNI_ALLOC()) != NULL) { MK_MSG_ORIG(stat, UNI_STATUS_ENQ, c->cref, !c->mine); (void)uni_send_output(stat, c->uni); UNI_FREE(stat); } TIMER_START_CALL(c, t322, c->uni->timer322); c->se_active = 1; } /* * Link-establish.indication in U10 * * Q.2971:Call-Control-U 19-20/39 * Q.2971:Call-Control-N 20-22/39 */ static void un10_link_establish_indication(struct call *c) { int act = 0; struct party *p; if (c->type == CALL_ROOT || c->type == CALL_LEAF) { TAILQ_FOREACH(p, &c->parties, link) if (p->state == UNI_EPSTATE_ACTIVE) { act = 1; uni_enq_party(p, SIGP_STATUS_ENQUIRY_request, 0, NULL, NULL); } if (act) return; } call_se(c); } /* * Link-establish.indication in NOT U10/U11/U12 N10/N11/N12 * * Q.2971:Call-Control-U 36/39 * Q.2971:Call-Control-N 37/39 */ static void unx_link_establish_indication(struct call *c) { call_se(c); } /* * Link-establish.confirm in U10 or N10 * * Q.2971:Call-Control-U 19/39 * Q.2971:Call-Control-N 20/39 */ static void un10_link_establish_confirm(struct call *c) { struct party *p; if (c->type == CALL_ROOT || c->type == CALL_LEAF) { TAILQ_FOREACH(p, &c->parties, link) uni_enq_party(p, SIGP_STATUS_ENQUIRY_request, 0, NULL, NULL); return; } call_se(c); } /* * STATUS ENQ from party * * Q.2971:Call-Control-U 21/39 * Q.2971:Call-Control-U 25/39 */ static void unx_send_party_status_enq(struct call *c, struct uni_all *u) { if (c->se_active) { uni_delenq_sig(c->uni, SIG_CALL, c, NULL, SIGC_SEND_STATUS_ENQ, 0, NULL, u); return; } c->stat_epref = u->u.status_enq.epref; (void)uni_send_output(u, c->uni); UNI_FREE(u); TIMER_START_CALL(c, t322, c->uni->timer322); c->se_active = 1; } /**********************************************************************/ static void make_drop_cause(struct call *c, struct uni_ie_cause *cause) { if (!IE_ISGOOD(*cause)) { /* 9.5.7.1 paragraph 2 */ if (IE_ISPRESENT(*cause)) MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_IE_INV); else MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_MANDAT); c->uni->cause.u.ie.len = 1; c->uni->cause.u.ie.ie[0] = UNI_IE_CAUSE; c->uni->cause.h.present |= UNI_CAUSE_IE_P; } else if (!IE_ISGOOD(c->uni->cause)) c->uni->cause = *cause; } /* * Drop-party.indication from Party-Control in any state. * * Q.2971:Call-Control-U 23/39 */ static void ux_drop_party_indication(struct call *c, struct uni_msg *api) { struct uniapi_drop_party_indication *drop = uni_msg_rptr(api, struct uniapi_drop_party_indication *); if (uni_party_act_count(c, 2) == 0) { if (c->cstate != CALLST_U11) { make_drop_cause(c, &drop->drop.cause); clear_callD(c); } uni_msg_destroy(api); return; } c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_DROP_PARTY_indication, 0, api); } /* * Drop-party.indication from Party-Control in any state. * * Q.2971:Call-Control-N 23/39 */ static void nx_drop_party_indication(struct call *c, struct uni_msg *api) { struct uniapi_drop_party_indication *drop = uni_msg_rptr(api, struct uniapi_drop_party_indication *); if (uni_party_act_count(c, 0) == 0) { if (uni_party_act_count(c, 1) == 0) { if (c->cstate != CALLST_U11) { make_drop_cause(c, &drop->drop.cause); clear_callD(c); } uni_msg_destroy(api); } else { c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_DROP_PARTY_indication, 0, api); set_call_state(c, CALLST_N7); } } else { c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_DROP_PARTY_indication, 0, api); } } /* * Drop-party-ack.indication from Party-Control in any state. * * Q.2971:Call-Control-U 23/39 */ static void ux_drop_party_ack_indication(struct call *c, struct uni_msg *api) { struct uniapi_drop_party_ack_indication *drop = uni_msg_rptr(api, struct uniapi_drop_party_ack_indication *); if (uni_party_act_count(c, 2) == 0) { if (c->cstate != CALLST_U11) { make_drop_cause(c, &drop->drop.cause); clear_callD(c); } uni_msg_destroy(api); return; } c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_DROP_PARTY_ACK_indication, 0, api); } /* * Drop-party-ack.indication from Party-Control in any state. * * Q.2971:Call-Control-N 23/39 */ static void nx_drop_party_ack_indication(struct call *c, struct uni_msg *api) { struct uniapi_drop_party_ack_indication *drop = uni_msg_rptr(api, struct uniapi_drop_party_ack_indication *); if (uni_party_act_count(c, 0) == 0) { if (uni_party_act_count(c, 1) == 0) { if (c->cstate != CALLST_U11) { make_drop_cause(c, &drop->drop.cause); clear_callD(c); } uni_msg_destroy(api); } else { c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_DROP_PARTY_ACK_indication, 0, api); set_call_state(c, CALLST_N7); } } else { c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_DROP_PARTY_ACK_indication, 0, api); } } /* * Add-party-rej.indication from Party-Control in any state. * * Q.2971:Call-Control-U 23/39 */ static void ux_add_party_rej_indication(struct call *c, struct uni_msg *api) { struct uniapi_add_party_rej_indication *rej = uni_msg_rptr(api, struct uniapi_add_party_rej_indication *); if (uni_party_act_count(c, 2) == 0) { if (c->cstate != CALLST_U11) { make_drop_cause(c, &rej->rej.cause); clear_callD(c); } uni_msg_destroy(api); return; } c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_ADD_PARTY_REJ_indication, 0, api); } /* * Add-party-rej.indication from Party-Control in any state. * * Q.2971:Call-Control-N 23/39 */ static void nx_add_party_rej_indication(struct call *c, struct uni_msg *api) { struct uniapi_add_party_rej_indication *rej = uni_msg_rptr(api, struct uniapi_add_party_rej_indication *); if (uni_party_act_count(c, 0) == 0) { if (uni_party_act_count(c, 1) == 0) { if (c->cstate != CALLST_U11) { make_drop_cause(c, &rej->rej.cause); clear_callD(c); } uni_msg_destroy(api); } else { c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_ADD_PARTY_REJ_indication, 0, api); set_call_state(c, CALLST_N7); } } else { c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_ADD_PARTY_REJ_indication, 0, api); } } /* * Add-party.request from API in U4 or U10 * * Q.2971:Call-Control-U 9-10/39 (U4) * Q.2971:Call-Control-U 21/39 (U10) * Q.2971:Call-Control-N 12/39 (N7) * Q.2971:Call-Control-N 22/39 (N10) */ static void unx_add_party_request(struct call *c, struct uni_msg *msg, uint32_t cookie) { struct uniapi_add_party_request *add = uni_msg_rptr(msg, struct uniapi_add_party_request *); struct party *p; if (IE_ISGOOD(add->add.epref)) { if (add->add.epref.flag != 0) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_IE, cookie); return; } p = uni_find_partyx(c, add->add.epref.epref, 1); if (p != NULL) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_EPREF_INUSE, cookie); return; } } else if (!IE_ISPRESENT(add->add.epref)) { allocate_epref(c, &add->add.epref); if (!IE_ISPRESENT(add->add.epref)) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_EPREF_INUSE, cookie); return; } } else { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_IE, cookie); return; } if ((p = uni_create_partyx(c, add->add.epref.epref, 1, cookie)) == NULL) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_NOMEM, cookie); return; } uni_enq_party(p, SIGP_ADD_PARTY_request, cookie, msg, NULL); } /* * Add-party-ack.request from API in U10/N10 * * Q.2971:Call-Control-U 21/39 * Q.2971:Call-Control-N 22/39 */ static void un10_add_party_ack_request(struct call *c, struct uni_msg *msg, uint32_t cookie) { struct uniapi_add_party_ack_request *ack = uni_msg_rptr(msg, struct uniapi_add_party_ack_request *); struct party *p; if (!IE_ISGOOD(ack->ack.epref)) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_IE, cookie); return; } if (ack->ack.epref.flag != 1) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_IE, cookie); return; } if ((p = uni_find_partyx(c, ack->ack.epref.epref, 0)) == NULL) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_PARTY, cookie); return; } uni_enq_party(p, SIGP_ADD_PARTY_ACK_request, cookie, msg, NULL); } /* * Party-alerting.request from API in U7/U8/U10 * * Q.2971:Call-Control-U 14/39 U7 * Q.2971:Call-Control-U 15/39 U8 * Q.2971:Call-Control-U 21/39 U10 * Q.2971:Call-Control-N 8/39 N4 * Q.2971:Call-Control-N 22/39 N10 */ static void unx_party_alerting_request(struct call *c, struct uni_msg *msg, uint32_t cookie) { struct uniapi_party_alerting_request *alert = uni_msg_rptr(msg, struct uniapi_party_alerting_request *); struct party *p; if (!IE_ISGOOD(alert->alert.epref)) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_IE, cookie); return; } if (alert->alert.epref.flag != 1) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_IE, cookie); return; } if ((p = uni_find_partyx(c, alert->alert.epref.epref, 0)) == NULL) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_PARTY, cookie); return; } uni_enq_party(p, SIGP_PARTY_ALERTING_request, cookie, msg, NULL); } /* * Add-party-rej.request from API in U7/U8/U10/N4/N10 * * Q.2971:Call-Control-U 14/39 U7 * Q.2971:Call-Control-U 15/39 U8 * Q.2971:Call-Control-U 21/39 U10 * Q.2971:Call-Control-N 8/39 N4 * Q.2971:Call-Control-N 22/39 N10 */ static void unx_add_party_rej_request(struct call *c, struct uni_msg *msg, uint32_t cookie) { struct uniapi_add_party_rej_request *rej = uni_msg_rptr(msg, struct uniapi_add_party_rej_request *); struct party *p; if (!IE_ISGOOD(rej->rej.epref)) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_IE, cookie); return; } if (rej->rej.epref.flag != 1) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_IE, cookie); return; } if ((p = uni_find_partyx(c, rej->rej.epref.epref, 0)) == NULL) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_PARTY, cookie); return; } uni_enq_party(p, SIGP_ADD_PARTY_REJ_request, cookie, msg, NULL); } /* * Drop-party.request from API in U1-U10 * * Q.2971:Call-Control-U 21/39 U10 * Q.2971:Call-Control-U 26/39 U1-U9 * Q.2971:Call-Control-N 22/39 N10 * Q.2971:Call-Control-N 27/39 N1-N9 */ static void unx_drop_party_request(struct call *c, struct uni_msg *msg, uint32_t cookie) { struct uniapi_drop_party_request *drop = uni_msg_rptr(msg, struct uniapi_drop_party_request *); struct party *p; if (!IE_ISGOOD(drop->drop.epref)) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_IE, cookie); return; } if ((p = uni_find_partyx(c, drop->drop.epref.epref, !drop->drop.epref.flag)) == NULL) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_PARTY, cookie); return; } uni_enq_party(p, SIGP_DROP_PARTY_request, cookie, msg, NULL); } /* * Drop-party-ack.request from API in U1-U10 * * Q.2971:Call-Control-U 21/39 U10 * Q.2971:Call-Control-U 26/39 U1-U9 * Q.2971:Call-Control-N 22/39 N10 * Q.2971:Call-Control-N 27/39 N1-N9 */ static void unx_drop_party_ack_request(struct call *c, struct uni_msg *msg, uint32_t cookie) { struct uniapi_drop_party_ack_request *ack = uni_msg_rptr(msg, struct uniapi_drop_party_ack_request *); struct party *p; if (!IE_ISGOOD(ack->ack.epref)) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_IE, cookie); return; } if ((p = uni_find_partyx(c, ack->ack.epref.epref, !ack->ack.epref.flag)) == NULL) { uni_msg_destroy(msg); uniapi_call_error(c, UNIAPI_ERROR_BAD_PARTY, cookie); return; } uni_enq_party(p, SIGP_DROP_PARTY_ACK_request, cookie, msg, NULL); } /* * ADD PARTY in U7/U8/U10 * * Q.2971:Call-Control-U 14/39 U7 * Q.2971:Call-Control-U 15/39 U8 * Q.2971:Call-Control-U 21/39 U10 * Q.2971:Call-Control-N 8/39 N4 * Q.2971:Call-Control-N 21/39 N10 * * Body already decoded * XXX check EPREF flag */ static void unx_add_party(struct call *c, struct uni_msg *m, struct uni_all *u, int legal) { struct uni_all *resp; struct uni_ierr *e1; struct party *p = NULL; enum verify vfy; uni_mandate_epref(c->uni, &u->u.add_party.epref); MANDATE_IE(c->uni, u->u.add_party.called, UNI_IE_CALLED); /* * Do part of the verify handish: according to 9.5.7.2 we must send * an ADD_PARTY_REJ if mandatory IEs are bad or missing instead of * clearing the call. But we must send a STATUS, if it is the EPREF! */ if (IE_ISGOOD(u->u.add_party.epref)) { c->uni->cause.u.ie.len = 0; FOREACH_ERR(e1, c->uni) { if (e1->err == UNI_IERR_MIS) { MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_MANDAT); goto rej; } } FOREACH_ERR(e1, c->uni) { if (e1->man && e1->ie != UNI_IE_EPREF && e1->act == UNI_IEACT_DEFAULT) { MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_IE_INV); rej: uni_vfy_collect_ies(c->uni); if ((resp = UNI_ALLOC()) != NULL) { MK_MSG_RESP(resp, UNI_ADD_PARTY_REJ, &u->u.hdr.cref); MK_IE_EPREF(resp->u.add_party_rej.epref, u->u.add_party.epref.epref, !u->u.add_party.epref.flag); resp->u.add_party_rej.cause = c->uni->cause; unx_send_add_party_rej(c, resp); } goto ignore; } } p = uni_find_partyx(c, u->u.add_party.epref.epref, u->u.add_party.epref.flag); } vfy = uni_verify(c->uni, u->u.hdr.act); switch (vfy) { case VFY_CLR: uni_vfy_collect_ies(c->uni); clear_callD(c); goto ignore; case VFY_RAIM: case VFY_RAI: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &u->u.add_party.epref, p ? p->state : UNI_EPSTATE_NULL); /* FALLTHRU */ case VFY_I: goto ignore; case VFY_RAP: case VFY_RAPU: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &u->u.add_party.epref, UNI_EPSTATE_ADD_RCVD); case VFY_OK: /* FALLTHRU */ break; } if (!legal) { uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.add_party.epref, -1); return; } if (IE_ISGOOD(u->u.add_party.epref) && p == NULL && u->u.add_party.epref.flag) { IE_SETERROR(u->u.add_party.epref); UNI_SAVE_IERR(&c->uni->cx, UNI_IE_EPREF, u->u.add_party.epref.h.act, UNI_IERR_BAD); } if (!IE_ISGOOD(u->u.add_party.epref)) { /* 9.5.3.2.2 */ if (vfy == VFY_OK) { MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_IE_INV); uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), NULL, 0); } goto ignore; } if (p == NULL && (p = uni_create_party(c, &u->u.add_party.epref)) == NULL) goto ignore; uni_enq_party(p, SIGP_ADD_PARTY, 0, m, u); return; ignore: uni_msg_destroy(m); UNI_FREE(u); } /* * ADD PARTY ACKNOWLEDGE * * Q.2971:Call-Control-U 21/39 U10 * Q.2971:Call-Control-N 15/39 N8 * Q.2971:Call-Control-N 22/39 N10 */ static void un10n8_add_party_ack(struct call *c, struct uni_msg *m, struct uni_all *u, int legal) { struct party *p = NULL; if (IE_ISGOOD(u->u.add_party_ack.epref)) { if (u->u.add_party_ack.epref.flag == 0) { IE_SETERROR(u->u.add_party_ack.epref); UNI_SAVE_IERR(&c->uni->cx, UNI_IE_EPREF, u->u.add_party_ack.epref.h.act, UNI_IERR_BAD); } else { p = uni_find_partyx(c, u->u.add_party_ack.epref.epref, 1); if (p == NULL) { respond_drop_party_ack(c, &u->u.add_party_ack.epref, UNI_CAUSE_ENDP_INV); goto ignore; } } } uni_mandate_epref(c->uni, &u->u.add_party_ack.epref); switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: uni_vfy_collect_ies(c->uni); clear_callD(c); goto ignore; case VFY_RAIM: case VFY_RAI: report: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &u->u.add_party_ack.epref, p ? p->state : UNI_EPSTATE_NULL); case VFY_I: goto ignore; case VFY_RAP: case VFY_RAPU: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &u->u.add_party_ack.epref, p ? UNI_EPSTATE_ACTIVE : UNI_EPSTATE_NULL); if (!IE_ISGOOD(u->u.party_alerting.epref)) /* See below */ goto ignore; break; case VFY_OK: if (!IE_ISGOOD(u->u.party_alerting.epref)) /* this happens when the EPREF has bad format. * The rules require us the message to be ignored * (9.5.3.2.2e) and to report status. */ goto report; break; } if (legal) { /* p is != NULL here */ uni_enq_party(p, SIGP_ADD_PARTY_ACK, 0, m, u); return; } if (p == NULL) /* Q.2971 9.5.3.2.3a) */ respond_drop_party_ack(c, &u->u.add_party_ack.epref, UNI_CAUSE_ENDP_INV); else uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.add_party_ack.epref, p->state); ignore: uni_msg_destroy(m); UNI_FREE(u); } /* * Make the EPREF action default */ static void default_act_epref(struct uni *uni, struct uni_ie_epref *epref) { struct uni_ierr *e; FOREACH_ERR(e, uni) if (e->ie == UNI_IE_EPREF) { e->act = UNI_IEACT_DEFAULT; break; } epref->h.act = UNI_IEACT_DEFAULT; } /* * PARTY ALERTING message * * Q.2971:Call-Control-U 9/39 U4 * Q.2971:Call-Control-U 21/39 U10 * Q.2971:Call-Control-N 12/39 N7 * Q.2971:Call-Control-N 15/39 N8 * Q.2971:Call-Control-N 22/39 N10 */ static void unx_party_alerting(struct call *c, struct uni_msg *m, struct uni_all *u, int legal) { struct party *p = NULL; if (IE_ISGOOD(u->u.party_alerting.epref)) { if (u->u.party_alerting.epref.flag == 0) { IE_SETERROR(u->u.party_alerting.epref); UNI_SAVE_IERR(&c->uni->cx, UNI_IE_EPREF, u->u.party_alerting.epref.h.act, UNI_IERR_BAD); } else { p = uni_find_partyx(c, u->u.party_alerting.epref.epref, 1); if (p == NULL) { respond_drop_party_ack(c, &u->u.party_alerting.epref, UNI_CAUSE_ENDP_INV); goto ignore; } } } uni_mandate_epref(c->uni, &u->u.party_alerting.epref); switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: uni_vfy_collect_ies(c->uni); clear_callD(c); goto ignore; case VFY_RAIM: case VFY_RAI: report: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &u->u.party_alerting.epref, p ? p->state : UNI_EPSTATE_NULL); case VFY_I: goto ignore; case VFY_RAP: case VFY_RAPU: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &u->u.party_alerting.epref, p ? UNI_EPSTATE_ALERT_RCVD : UNI_EPSTATE_NULL); if (!IE_ISGOOD(u->u.party_alerting.epref)) /* See below */ goto ignore; break; case VFY_OK: if (!IE_ISGOOD(u->u.party_alerting.epref)) /* The rules require us the message to be ignored * (9.5.3.2.2e) and to report status. */ goto report; break; } if (legal) { /* p is != NULL here */ uni_enq_party(p, SIGP_PARTY_ALERTING, 0, m, u); return; } if (p == NULL) /* Q.2971 9.5.3.2.3a) */ respond_drop_party_ack(c, &u->u.party_alerting.epref, UNI_CAUSE_ENDP_INV); else uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.party_alerting.epref, p->state); ignore: uni_msg_destroy(m); UNI_FREE(u); } /* * Handle a bad/missing cause in a DROP_PARTY_ACK or ADD_PARTY_REJ * * If the IE is missing or bad and the action is defaulted handle as * cause #1 according to 9.5.7.1/2. * Otherwise keep the IE. */ static void handle_bad_drop_cause(struct call *c, struct uni_ie_cause *cause, int mkcause) { if (IE_ISGOOD(*cause)) return; if (!IE_ISPRESENT(*cause)) { /* 9.5.7.1 */ /* cannot make cause here because we need the 96 error */ uni_vfy_remove_cause(c->uni); return; } if (cause->h.act != UNI_IEACT_DEFAULT) return; /* 9.5.7.2 */ uni_vfy_remove_cause(c->uni); if (mkcause) MK_IE_CAUSE(*cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_UNSPEC); } /* * ADD PARTY REJ from party control * Q.2971:Call-Control-U 21/39 * Q.2971:Call-Control-U 24/39 */ static void unx_send_add_party_rej(struct call *c, struct uni_all *u) { if (uni_party_act_count(c, 2) == 0) { if (c->cstate != CALLST_U11 && c->cstate != CALLST_N12) { c->uni->cause = u->u.add_party_rej.cause; clear_callD(c); } } else (void)uni_send_output(u, c->uni); UNI_FREE(u); } /* * ADD_PARTY_REJECT in U4/U10 * * Q.2971:Call-Control-U 9/39 U4 * Q.2971:Call-Control-U 21/39 U10 * Q.2971:Call-Control-N 12/39 N7 * Q.2971:Call-Control-N 15/39 N8 * Q.2971:Call-Control-N 22/39 N10 */ static void unx_add_party_rej(struct call *c, struct uni_msg *m, struct uni_all *u, int legal) { struct uni_add_party_rej *ar = &u->u.add_party_rej; struct party *p; if (IE_ISGOOD(ar->epref)) { p = uni_find_partyx(c, ar->epref.epref, ar->epref.flag); if (p == NULL) goto ignore; if (legal) { handle_bad_drop_cause(c, &ar->cause, 0); uni_vfy_remove_unknown(c->uni); switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: goto clear; case VFY_RAIM: case VFY_RAI: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &ar->epref, p->state); case VFY_I: goto ignore; case VFY_RAPU: uni_vfy_collect_ies(c->uni); break; case VFY_RAP: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &ar->epref, p->state); case VFY_OK: break; } uni_enq_party(p, SIGP_ADD_PARTY_REJ, 0, m, u); return; } uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &ar->epref, -1); return; } /* Q.2971: 9.5.3.2.1 last paragraph * 9.5.3.2.2 second to last paragraph * Make the action indicator default. */ default_act_epref(c->uni, &ar->epref); if (!IE_ISPRESENT(ar->epref)) uni_mandate_ie(c->uni, UNI_IE_EPREF); (void)uni_verify(c->uni, u->u.hdr.act); clear: uni_vfy_collect_ies(c->uni); clear_callD(c); ignore: uni_msg_destroy(m); UNI_FREE(u); } /* * DROP_PARTY * * Q.2971:Call-Control-U 26/39 Ux * Q.2971:Call-Control-U 21/39 U10 * Q.2971:Call-Control-N 27/39 Nx * Q.2971:Call-Control-N 22/39 N10 */ static void unx_drop_party(struct call *c, struct uni_msg *m, struct uni_all *u, int legal) { struct uni_drop_party *dp = &u->u.drop_party; struct party *p; struct uni_ierr *e; if (IE_ISGOOD(dp->epref)) { p = uni_find_partyx(c, dp->epref.epref, dp->epref.flag); if (p == NULL) { respond_drop_party_ack(c, &dp->epref, UNI_CAUSE_ENDP_INV); goto ignore; } handle_bad_drop_cause(c, &dp->cause, 0); uni_vfy_remove_unknown(c->uni); switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: goto clear; case VFY_RAIM: case VFY_RAI: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &u->u.drop_party.epref, p->state); case VFY_I: goto ignore; case VFY_RAPU: uni_vfy_collect_ies(c->uni); break; case VFY_RAP: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &dp->epref, UNI_EPSTATE_DROP_RCVD); case VFY_OK: break; } if (legal) { uni_enq_party(p, SIGP_DROP_PARTY, 0, m, u); return; } uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &dp->epref, -1); goto ignore; } /* Q.2971: 9.5.3.2.1 last paragraph * 9.5.3.2.2 second to last paragraph * Make the action indicator default. */ FOREACH_ERR(e, c->uni) if (e->ie == UNI_IE_EPREF) { e->act = UNI_IEACT_DEFAULT; break; } dp->epref.h.act = UNI_IEACT_DEFAULT; if (!IE_ISPRESENT(dp->epref)) uni_mandate_ie(c->uni, UNI_IE_EPREF); (void)uni_verify(c->uni, u->u.hdr.act); clear: uni_vfy_collect_ies(c->uni); clear_callD(c); uni_msg_destroy(m); UNI_FREE(u); return; ignore: uni_msg_destroy(m); UNI_FREE(u); } /* * DROP_PARTY_ACK * * Q.2971:Call-Control-U 26/39 Ux * Q.2971:Call-Control-U 21/39 U10 * Q.2971:Call-Control-N 27/39 Nx * Q.2971:Call-Control-N 22/39 N10 */ static void unx_drop_party_ack(struct call *c, struct uni_msg *m, struct uni_all *u, int legal) { struct party *p; struct uni_drop_party_ack *ack = &u->u.drop_party_ack; if (IE_ISGOOD(u->u.drop_party_ack.epref)) { p = uni_find_partyx(c, ack->epref.epref, ack->epref.flag); if (p != NULL) { handle_bad_drop_cause(c, &ack->cause, 1); uni_vfy_remove_unknown(c->uni); switch (uni_verify(c->uni, u->u.hdr.act)) { case VFY_CLR: goto clear; case VFY_RAIM: case VFY_RAI: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &ack->epref, p->state); case VFY_I: goto ignore; case VFY_RAP: uni_respond_status_verify(c->uni, &u->u.hdr.cref, map_callstate(c->cstate), &ack->epref, UNI_EPSTATE_NULL); case VFY_RAPU: case VFY_OK: break; } if (legal) { uni_enq_party(p, SIGP_DROP_PARTY_ACK, 0, m, u); return; } uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &ack->epref, -1); } goto ignore; } /* Q.2971: 9.5.3.2.1 last paragraph * 9.5.3.2.2 second to last paragraph */ (void)uni_verify(c->uni, u->u.hdr.act); MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_IE_INV); clear: uni_vfy_collect_ies(c->uni); clear_callD(c); uni_msg_destroy(m); UNI_FREE(u); return; ignore: uni_msg_destroy(m); UNI_FREE(u); } /**********************************************************************/ /* * Bad or unrecognized message. * * Q.2971:Call-Control-U 35/39 */ void uni_bad_message(struct call *c, struct uni_all *u, u_int cause, struct uni_ie_epref *epref, int ps) { struct uni_all *resp; struct party *p; if ((u->u.hdr.act == UNI_MSGACT_CLEAR && (c->cstate == CALLST_U11 || c->cstate == CALLST_U12 || c->cstate == CALLST_N11 || c->cstate == CALLST_N12)) || u->u.hdr.act == UNI_MSGACT_IGNORE) return; MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, cause); ADD_CAUSE_MTYPE(c->uni->cause, u->mtype); if (u->u.hdr.act == UNI_MSGACT_CLEAR) { clear_callD(c); return; } /* * Send STATUS */ if ((resp = UNI_ALLOC()) != NULL) { MK_MSG_RESP(resp, UNI_STATUS, &u->u.hdr.cref); MK_IE_CALLSTATE(resp->u.status.callstate, map_callstate(c->cstate)); resp->u.status.cause = c->uni->cause; if (epref != NULL && IE_ISGOOD(*epref)) { MK_IE_EPREF(resp->u.status.epref, epref->epref, !epref->flag); if (ps == -1) { p = uni_find_party(c, epref); if (p == NULL) ps = UNI_EPSTATE_NULL; else ps = p->state; } MK_IE_EPSTATE(resp->u.status.epstate, ps); } (void)uni_send_output(resp, c->uni); UNI_FREE(resp); } } /**********************************************************************/ /* * Unknown message in any state. * * Q.2971:Call-Control 35/39 * Q.2971:Call-Control 36/39 */ static void unx_unknown(struct call *c, struct uni_msg *m, struct uni_all *u) { /* * Unrecognized message. Cannot call verify here, because * it doesn't know about unrecognized messages. */ if (u->u.hdr.act == UNI_MSGACT_CLEAR) { MK_IE_CAUSE(c->uni->cause, UNI_CAUSE_LOC_USER, UNI_CAUSE_MTYPE_NIMPL); ADD_CAUSE_MTYPE(c->uni->cause, u->mtype); clear_callD(c); } else if(u->u.hdr.act == UNI_MSGACT_IGNORE) { ; } else { (void)uni_decode_body(m, u, &c->uni->cx); uni_bad_message(c, u, UNI_CAUSE_MTYPE_NIMPL, &u->u.unknown.epref, -1); } uni_msg_destroy(m); UNI_FREE(u); } /**********************************************************************/ void uni_sig_call(struct call *c, enum call_sig sig, uint32_t cookie, struct uni_msg *msg, struct uni_all *u) { if (sig >= SIGC_END) { VERBOSE(c->uni, UNI_FAC_ERR, 1, "Signal %d outside of range to Call-Control", sig); if (msg) uni_msg_destroy(msg); if (u) UNI_FREE(u); return; } VERBOSE(c->uni, UNI_FAC_CALL, 1, "Signal %s in state %s of call %u/%s" "; cookie %u", call_sigs[sig], callstates[c->cstate].name, c->cref, c->mine ? "mine" : "his", cookie); switch (sig) { case SIGC_LINK_RELEASE_indication: if (c->cstate == CALLST_U10 || c->cstate == CALLST_N10) /* Q.2971:Call-Control-U 36/39 */ /* Q.2971:Call-Control-N 20/39 */ un10_link_release_indication(c); else /* Q.2971:Call-Control-U 36/39 */ /* Q.2971:Call-Control-N 37/39 */ unx_link_release_indication(c); break; case SIGC_LINK_ESTABLISH_ERROR_indication: if (c->cstate != CALLST_U10 && c->cstate != CALLST_N10) { VERBOSE(c->uni, UNI_FAC_ERR, 1, "link-establish-error.indication in cs=%s", callstates[c->cstate].name); break; } /* Q.2971:Call-Control-U 19/39 */ /* Q.2971:Call-Control-N 20/39 */ un10_link_establish_error_indication(c); break; case SIGC_LINK_ESTABLISH_indication: switch (c->cstate) { case CALLST_U1: case CALLST_N1: case CALLST_U3: case CALLST_N3: case CALLST_U4: case CALLST_N4: case CALLST_U6: case CALLST_N6: case CALLST_U7: case CALLST_N7: case CALLST_U8: case CALLST_N8: case CALLST_U9: case CALLST_N9: /* Q.2971:Call-Control-U 36/39 */ /* Q.2971:Call-Control-N 37/39 */ unx_link_establish_indication(c); break; case CALLST_U10: case CALLST_N10: /* Q.2971:Call-Control-U 19/39 */ /* Q.2971:Call-Control-N 20/39 */ un10_link_establish_indication(c); break; case CALLST_U11: case CALLST_N11: case CALLST_U12: case CALLST_N12: /* Q.2971:Call-Control-U 36/39 */ /* Q.2971:Call-Control-N 37/39 */ break; default: VERBOSE(c->uni, UNI_FAC_ERR, 1, "link-establish.indication in cs=%s", callstates[c->cstate].name); } break; case SIGC_LINK_ESTABLISH_confirm: if (c->cstate != CALLST_U10 && c->cstate != CALLST_N10) { VERBOSE(c->uni, UNI_FAC_ERR, 1, "link-establish.confirm in cs=%s", callstates[c->cstate].name); break; } /* Q.2971:Call-Control-U 19/39 */ /* Q.2971:Call-Control-N 20/39 */ un10_link_establish_confirm(c); break; case SIGC_UNKNOWN: /* Q.2971:Call-Control 35/39 */ /* Q.2971:Call-Control 36/39 */ unx_unknown(c, msg, u); break; case SIGC_SETUP: if (c->cstate != CALLST_NULL) { (void)uni_decode_body(msg, u, &c->uni->cx); uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.setup.epref, -1); goto drop; } if (c->uni->proto == UNIPROTO_UNI40N) /* Q.2971:Call-Control-N 4/39 */ un0_setup(c, msg, u, CALLST_N1); else /* Q.2971:Call-Control-U 4/39 */ un0_setup(c, msg, u, CALLST_U6); break; case SIGC_CALL_PROC: if (c->cstate == CALLST_U1) { /* Q.2971:Call-Control-U 6/39 */ u1n6_call_proc(c, msg, u, CALLST_U3); break; } if (c->cstate == CALLST_N6) { /* Q.2971:Call-Control-N 11/39 */ u1n6_call_proc(c, msg, u, CALLST_N9); break; } (void)uni_decode_body(msg, u, &c->uni->cx); uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.call_proc.epref, -1); goto drop; case SIGC_ALERTING: if (c->cstate == CALLST_U1 || c->cstate == CALLST_U3) { /* Q.2971:Call-Control-U 37/39 (U1) */ /* Q.2971:Call-Control-U 7/39 (U3) */ unx_alerting(c, msg, u, CALLST_U4); break; } if (c->cstate == CALLST_N6) { /* Q.2971:Call-Control-N 9/39 (N6) */ /* Q.2971:Call-Control-N 17/39 (N9) */ unx_alerting(c, msg, u, CALLST_N7); break; } (void)uni_decode_body(msg, u, &c->uni->cx); uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.alerting.epref, -1); goto drop; case SIGC_CONNECT: if (c->cstate == CALLST_U1 || c->cstate == CALLST_U3 || c->cstate == CALLST_U4) { /* Q.2971:Call-Control-U 7-8/39 (U3) */ /* Q.2971:Call-Control-U 11/39 (U4) */ /* Q.2971:Call-Control-U 37/39 (U1) */ unx_connect(c, msg, u, CALLST_U10); break; } if (c->cstate == CALLST_N6 || c->cstate == CALLST_N7 || c->cstate == CALLST_N9) { /* Q.2971:Call-Control-N 9-10/39 (N6) */ /* Q.2971:Call-Control-N 14/39 (N7) */ /* Q.2971:Call-Control-N 17/39 (N9) */ unx_connect(c, msg, u, CALLST_N8); break; } (void)uni_decode_body(msg, u, &c->uni->cx); uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.connect.epref, -1); goto drop; case SIGC_CONNECT_ACK: if (c->cstate == CALLST_U8) { /* Q.2971:Call-Control-U 15-16/39 */ u8_connect_ack(c, msg, u, CALLST_U10); break; } if (c->cstate == CALLST_N10) { /* Q.2971:Call-Control-N 18/39 */ n10_connect_ack(c, msg, u); break; } uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, NULL, 0); goto drop; case SIGC_RELEASE: switch (c->cstate) { default: uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, NULL, 0); goto drop; case CALLST_U11: case CALLST_N12: /* Q.2971:Call-Control-U 28/39 */ /* Q.2971:Call-Control-N 30/39 */ u11n12_release(c, msg, u); break; case CALLST_U1: case CALLST_U3: case CALLST_U4: case CALLST_U6: case CALLST_U7: case CALLST_U8: case CALLST_U9: case CALLST_U10: case CALLST_U12: /* Q.2971:Call-Control-U 25/39 */ unx_release(c, msg, u, CALLST_U12); break; case CALLST_N1: case CALLST_N3: case CALLST_N4: case CALLST_N6: case CALLST_N7: case CALLST_N8: case CALLST_N9: case CALLST_N10: case CALLST_N11: /* Q.2971:Call-Control-N 26/39 */ unx_release(c, msg, u, CALLST_N11); break; } break; case SIGC_RELEASE_COMPL: /* Q.2971:Call-Control-U 25/39 */ /* Q.2971:Call-Control-N 26/39 */ unx_release_compl(c, msg, u); break; case SIGC_NOTIFY: /* Q.2971:Call-Control-U 18/39 */ /* Q.2971:Call-Control-N 19/39 */ unx_notify(c, msg, u); break; case SIGC_STATUS: if (c->cstate == CALLST_U11 || c->cstate == CALLST_U12 || c->cstate == CALLST_N11 || c->cstate == CALLST_N12) { /* Q.2971:Call-Control-U 29/39 (U11) */ /* Q.2971:Call-Control-U 30/39 (U12) */ /* Q.2971:Call-Control-N 29/39 (N11) */ /* Q.2971:Call-Control-N 31/39 (N12) */ un11un12_status(c, msg, u); break; } /* Q.2971:Call-Control-U 32/39 */ /* Q.2971:Call-Control-N 33/39 */ unx_status(c, msg, u); break; case SIGC_STATUS_ENQ: /* Q.2971:Call-Control-U 31/39 */ /* Q.2971:Call-Control-N 32/39 */ unx_status_enq(c, msg, u); break; case SIGC_ADD_PARTY: (void)uni_decode_body(msg, u, &c->uni->cx); if (c->type != CALL_LEAF && c->type != CALL_ROOT) { uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.add_party.epref, UNI_EPSTATE_NULL); goto drop; } switch (c->cstate) { case CALLST_U7: case CALLST_U8: case CALLST_U10: case CALLST_N4: case CALLST_N10: /* Q.2971:Call-Control-U 14/39 U7 */ /* Q.2971:Call-Control-U 15/39 U8 */ /* Q.2971:Call-Control-U 21/39 U10 */ /* Q.2971:Call-Control-N 8/39 N4 */ /* Q.2971:Call-Control-N 21/39 N10 */ unx_add_party(c, msg, u, 1); break; default: unx_add_party(c, msg, u, 0); goto drop; } break; case SIGC_PARTY_ALERTING: (void)uni_decode_body(msg, u, &c->uni->cx); if (c->type != CALL_ROOT) { uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.party_alerting.epref, -1); goto drop; } switch (c->cstate) { default: /* Q.2971 9.5.3.2.3a) */ unx_party_alerting(c, msg, u, 0); break; case CALLST_U4: case CALLST_U10: /* Q.2971:Call-Control-U 9/39 U4 */ /* Q.2971:Call-Control-U 21/39 U10 */ /* Q.2971:Call-Control-N 12/39 N7 */ /* Q.2971:Call-Control-N 15/39 N8 */ /* Q.2971:Call-Control-N 22/39 N10 */ unx_party_alerting(c, msg, u, 1); break; } break; case SIGC_ADD_PARTY_ACK: (void)uni_decode_body(msg, u, &c->uni->cx); if (c->type != CALL_ROOT) { uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.add_party_rej.epref, -1); goto drop; } switch (c->cstate) { case CALLST_U10: /* Q.2971:Call-Control-U 21/39 U10 */ /* Q.2971:Call-Control-N 15/39 N8 */ /* Q.2971:Call-Control-N 22/39 N10 */ un10n8_add_party_ack(c, msg, u, 1); break; default: /* Q.2971 9.5.3.2.3a) */ un10n8_add_party_ack(c, msg, u, 0); break; } break; case SIGC_ADD_PARTY_REJ: (void)uni_decode_body(msg, u, &c->uni->cx); if (c->type != CALL_ROOT) { uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.add_party_rej.epref, -1); goto drop; } switch (c->cstate) { case CALLST_U4: case CALLST_U10: case CALLST_N7: case CALLST_N8: case CALLST_N10: /* Q.2971:Call-Control-U 9/39 U4 */ /* Q.2971:Call-Control-U 21/39 U10 */ /* Q.2971:Call-Control-N 12/39 N7 */ /* Q.2971:Call-Control-N 15/39 N8 */ /* Q.2971:Call-Control-N 22/39 N10 */ unx_add_party_rej(c, msg, u, 1); break; default: /* Q.2971: 9.5.3.2.3b */ unx_add_party_rej(c, msg, u, 0); break; } break; case SIGC_DROP_PARTY: (void)uni_decode_body(msg, u, &c->uni->cx); if (c->type != CALL_ROOT && c->type != CALL_LEAF) { uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.drop_party.epref, -1); goto drop; } switch (c->cstate) { case CALLST_U11: case CALLST_U12: case CALLST_N11: case CALLST_N12: /* Q.2971:Call-Control-U 28/39 U11 */ /* Q.2971:Call-Control-U 30/39 U12 */ /* Q.2971:Call-Control-N 29/39 N11 */ /* Q.2971:Call-Control-N 30/39 N12 */ goto drop; case CALLST_NULL: uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.drop_party.epref, UNI_EPSTATE_NULL); goto drop; case CALLST_U3: case CALLST_N3: /* L3MU_17_38 */ unx_drop_party(c, msg, u, 0); break; case CALLST_U8: if (c->uni->sb_tb) { /* L3MU_06_0[3-6] */ unx_drop_party(c, msg, u, 0); break; } /* FALLTHRU */ default: /* Q.2971:Call-Control-U 26/39 Ux */ /* Q.2971:Call-Control-U 21/39 U10 */ /* Q.2971:Call-Control-N 27/39 Nx */ /* Q.2971:Call-Control-N 21/39 N10 */ unx_drop_party(c, msg, u, 1); break; } break; case SIGC_DROP_PARTY_ACK: (void)uni_decode_body(msg, u, &c->uni->cx); if (c->type != CALL_ROOT && c->type != CALL_LEAF) { uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.drop_party_ack.epref, -1); goto drop; } switch (c->cstate) { case CALLST_U11: case CALLST_U12: /* Q.2971:Call-Control-U 28/39 U11 */ /* Q.2971:Call-Control-U 30/39 U12 */ /* Q.2971:Call-Control-N 29/39 N11 */ /* Q.2971:Call-Control-N 30/39 N12 */ goto drop; case CALLST_NULL: uni_bad_message(c, u, UNI_CAUSE_MSG_INCOMP, &u->u.drop_party.epref, UNI_EPSTATE_NULL); goto drop; case CALLST_U4: case CALLST_N4: case CALLST_U7: case CALLST_N7: case CALLST_U8: case CALLST_N8: case CALLST_U10: case CALLST_N10: /* Q.2971:Call-Control-U 26/39 Ux */ /* Q.2971:Call-Control-U 21/39 U10 */ /* Q.2971:Call-Control-N 27/39 Nx */ /* Q.2971:Call-Control-N 22/39 N10 */ unx_drop_party_ack(c, msg, u, 1); break; default: /* Q.2971 10.5 4th paragraph */ unx_drop_party_ack(c, msg, u, 0); break; } break; case SIGC_COBISETUP: /* XXX */ unx_unknown(c, msg, u); break; /* * User signals */ case SIGC_SETUP_request: if (c->cstate == CALLST_NULL) { /* Q.2971:Call-Control-U 4/39 (U0) */ /* Q.2971:Call-Control-N 4/39 (N0) */ if (c->uni->proto == UNIPROTO_UNI40N) un0_setup_request(c, msg, cookie, CALLST_N6); else un0_setup_request(c, msg, cookie, CALLST_U1); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "setup.request in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_SETUP_response: if (c->cstate == CALLST_U6 || c->cstate == CALLST_U9 || c->cstate == CALLST_U7) { /* Q.2971:Call-Control-U 13/39 (U6) */ /* Q.2971:Call-Control-U 14/39 (U7) */ /* Q.2971:Call-Control-U 17/39 (U9) */ unx_setup_response(c, msg, cookie, CALLST_U8); break; } if (c->cstate == CALLST_N1 || c->cstate == CALLST_N3 || c->cstate == CALLST_N4) { /* Q.2971:Call-Control-N 39/39 (N1) */ /* Q.2971:Call-Control-N 7/39 (N3) */ /* Q.2971:Call-Control-N 8/39 (N4) */ unx_setup_response(c, msg, cookie, CALLST_N10); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "setup.response in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_SETUP_COMPLETE_request: if (c->cstate == CALLST_N8) { /* Q.2971:Call-Control-N 15/39 (N8) */ n8_setup_compl_request(c, msg, cookie, CALLST_N10); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "setup_compl.request in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_PROCEEDING_request: if (c->cstate == CALLST_U6) { /* Q.2971:Call-Control-U 12/39 (U6) */ u6n1_proceeding_request(c, msg, cookie, CALLST_U9); break; } if (c->cstate == CALLST_N1) { /* Q.2971:Call-Control-N 6/39 (N1) */ u6n1_proceeding_request(c, msg, cookie, CALLST_N3); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "proceeding.request in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_ALERTING_request: if (c->cstate == CALLST_U6 || c->cstate == CALLST_U9) { /* Q.2971:Call-Control-U 13/39 (U6) */ /* Q.2971:Call-Control-U 17/39 (U9) */ unx_alerting_request(c, msg, cookie, CALLST_U7); break; } if (c->cstate == CALLST_N1 || c->cstate == CALLST_N3) { /* Q.2971:Call-Control-N 38/39 (N1) */ /* Q.2971:Call-Control-N 7/39 (N3) */ unx_alerting_request(c, msg, cookie, CALLST_N4); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "alerting.request in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_RELEASE_request: switch (c->cstate) { case CALLST_U1: case CALLST_U3: case CALLST_U4: case CALLST_U6: case CALLST_U7: case CALLST_U8: case CALLST_U9: case CALLST_U10: /* Q.2971:Call-Control-U 27/39 */ unx_release_request(c, msg, cookie, CALLST_U11); break; case CALLST_N1: case CALLST_N3: case CALLST_N4: case CALLST_N6: case CALLST_N7: case CALLST_N8: case CALLST_N9: case CALLST_N10: /* Q.2971:Call-Control-N 28/39 */ unx_release_request(c, msg, cookie, CALLST_N12); break; case CALLST_U11: case CALLST_U12: case CALLST_N11: case CALLST_N12: case CALLST_NULL: VERBOSE(c->uni, UNI_FAC_ERR, 1, "release.request in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; } break; case SIGC_RELEASE_response: if (c->cstate == CALLST_U6 || c->cstate == CALLST_U12 || c->cstate == CALLST_N1 || c->cstate == CALLST_N11) { /* Q.2971:Call-Control-U 12/39 (U6) */ /* Q.2971:Call-Control-U 30/39 (U12) */ /* Q.2971:Call-Control-N 6/39 (N1) */ /* Q.2971:Call-Control-N 29/39 (N11) */ unx_release_response(c, msg, cookie); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "release.response in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_NOTIFY_request: /* Q.2971:Call-Control-U 18/39 */ /* Q.2971:Call-Control-N 19/39 */ unx_notify_request(c, msg, cookie); break; case SIGC_STATUS_ENQUIRY_request: /* Q.2971:Call-Control-U 31/39 */ /* Q.2971:Call-Control-N 32/39 */ unx_status_enquiry_request(c, msg, cookie); break; case SIGC_ADD_PARTY_request: if (c->cstate == CALLST_U4 || c->cstate == CALLST_U10 || c->cstate == CALLST_N7 || c->cstate == CALLST_N10) { /* Q.2971:Call-Control-U 9-10/39 (U4) */ /* Q.2971:Call-Control-U 21/39 (U10) */ /* Q.2971:Call-Control-N 12/39 (N7) */ /* Q.2971:Call-Control-N 22/39 (N10) */ unx_add_party_request(c, msg, cookie); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "add-party.request in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_PARTY_ALERTING_request: if (c->cstate == CALLST_U7 || c->cstate == CALLST_U8 || c->cstate == CALLST_U10 || c->cstate == CALLST_N4 || c->cstate == CALLST_N10) { /* Q.2971:Call-Control-U 14/39 U7 */ /* Q.2971:Call-Control-U 15/39 U8 */ /* Q.2971:Call-Control-U 21/39 U10 */ /* Q.2971:Call-Control-N 8/39 N4 */ /* Q.2971:Call-Control-N 22/39 N10 */ unx_party_alerting_request(c, msg, cookie); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "party-alerting.request in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_ADD_PARTY_ACK_request: if (c->cstate == CALLST_U10 || c->cstate == CALLST_N10) { /* Q.2971:Call-Control-U 21/39 (U10) */ /* Q.2971:Call-Control-N 22/39 (N10)*/ un10_add_party_ack_request(c, msg, cookie); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "add-party-ack.request in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_ADD_PARTY_REJ_request: if (c->cstate == CALLST_U7 || c->cstate == CALLST_U8 || c->cstate == CALLST_U10 || c->cstate == CALLST_N4 || c->cstate == CALLST_N10) { /* Q.2971:Call-Control-U 14/39 U7 */ /* Q.2971:Call-Control-U 15/39 U8 */ /* Q.2971:Call-Control-U 21/39 U10 */ /* Q.2971:Call-Control-N 8/39 N4 */ /* Q.2971:Call-Control-N 22/39 N10 */ unx_add_party_rej_request(c, msg, cookie); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "add-party-rej.request in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_DROP_PARTY_request: if (c->cstate != CALLST_U11 && c->cstate != CALLST_U12 && c->cstate != CALLST_N11 && c->cstate != CALLST_N12 && c->cstate != CALLST_NULL) { /* Q.2971:Call-Control-U 21/39 U10 */ /* Q.2971:Call-Control-U 26/39 U1-U9 */ /* Q.2971:Call-Control-N 22/39 N10 */ /* Q.2971:Call-Control-N 27/39 N1-N9 */ unx_drop_party_request(c, msg, cookie); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "drop-party.request in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_DROP_PARTY_ACK_request: if (c->cstate != CALLST_U11 && c->cstate != CALLST_U12 && c->cstate != CALLST_N11 && c->cstate != CALLST_N12 && c->cstate != CALLST_NULL) { /* Q.2971:Call-Control-U 21/39 U10 */ /* Q.2971:Call-Control-U 26/39 U1-U9 */ /* Q.2971:Call-Control-N 22/39 N10 */ /* Q.2971:Call-Control-N 27/39 N1-N9 */ unx_drop_party_ack_request(c, msg, cookie); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "drop-party-ack.request in cs=%s", callstates[c->cstate].name); uniapi_call_error(c, UNIAPI_ERROR_BAD_CALLSTATE, cookie); uni_msg_destroy(msg); break; case SIGC_ABORT_CALL_request: { struct uni *uni = c->uni; uni_destroy_call(c, 0); uniapi_uni_error(uni, UNIAPI_OK, cookie, UNI_CALLSTATE_U0); break; } /* * Timers */ case SIGC_T301: if (c->cstate == CALLST_U4 || c->cstate == CALLST_N7) { /* Q.2971:Call-Control-U Missing */ /* Q.2971:Call-Control-N 14/39 */ u4n7_t301(c); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "T301 in cs=%s", callstates[c->cstate].name); break; case SIGC_T303: if (c->cstate == CALLST_U1 || c->cstate == CALLST_N6) { /* Q.2971:Call-Control-U 6/39 */ /* Q.2971:Call-Control-N 11/39 */ u1n6_t303(c); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "T303 in cs=%s", callstates[c->cstate].name); break; case SIGC_T308: if (c->cstate == CALLST_U11 || c->cstate == CALLST_N12) { /* Q.2971:Call-Control-U 28/39 */ /* Q.2971:Call-Control-N 30/39 */ u11n12_t308(c); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "T308 in cs=%s", callstates[c->cstate].name); break; case SIGC_T310: if (c->cstate == CALLST_U3 || c->cstate == CALLST_N9) { /* Q.2971:Call-Control-U 7/39 */ /* Q.2971:Call-Control-N 17/39 */ u3n9_t310(c); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "T310 in cs=%s", callstates[c->cstate].name); break; case SIGC_T313: if (c->cstate == CALLST_U8) { /* Q.2971:Call-Control-U 15/39 */ u8_t313(c); break; } VERBOSE(c->uni, UNI_FAC_ERR, 1, "T313 in cs=%s", callstates[c->cstate].name); break; case SIGC_T322: /* Q.2971:Call-Control-U 34/39 */ /* Q.2971:Call-Control-N 35/39 */ unx_t322(c); break; case SIGC_CALL_DELETE: CALL_FREE(c); break; /* * Party-Control */ case SIGC_DROP_PARTY_indication: if (c->uni->proto == UNIPROTO_UNI40U) /* Q.2971:Call-Control-U 23/39 */ ux_drop_party_indication(c, msg); else /* Q.2971:Call-Control-N 23/39 */ nx_drop_party_indication(c, msg); break; case SIGC_DROP_PARTY_ACK_indication: if (c->uni->proto == UNIPROTO_UNI40U) /* Q.2971:Call-Control-U 23/39 */ ux_drop_party_ack_indication(c, msg); else /* Q.2971:Call-Control-N 23/39 */ nx_drop_party_ack_indication(c, msg); break; case SIGC_ADD_PARTY_REJ_indication: if (c->uni->proto == UNIPROTO_UNI40U) /* Q.2971:Call-Control-U 23/39 */ ux_add_party_rej_indication(c, msg); else /* Q.2971:Call-Control-N 23/39 */ nx_add_party_rej_indication(c, msg); break; case SIGC_SEND_DROP_PARTY: /* Q.2971:Call-Control-U 21/39 */ /* Q.2971:Call-Control-U 25/39 */ if (uni_party_act_count(c, 2) != 0) (void)uni_send_output(u, c->uni); else if(c->cstate != CALLST_U11) { c->uni->cause = u->u.drop_party.cause; clear_callD(c); } UNI_FREE(u); break; case SIGC_SEND_DROP_PARTY_ACK: /* Q.2971:Call-Control-U 21/39 */ /* Q.2971:Call-Control-U 25/39 */ if (uni_party_act_count(c, 2) != 0) (void)uni_send_output(u, c->uni); else if (c->cstate != CALLST_U11) { c->uni->cause = u->u.drop_party_ack.cause; clear_callD(c); } UNI_FREE(u); break; case SIGC_SEND_ADD_PARTY_REJ: /* Q.2971:Call-Control-U 21/39 */ /* Q.2971:Call-Control-U 24/39 */ unx_send_add_party_rej(c, u); break; case SIGC_SEND_STATUS_ENQ: /* Q.2971:Call-Control-U 21/39 */ /* Q.2971:Call-Control-U 25/39 */ unx_send_party_status_enq(c, u); break; case SIGC_PARTY_DESTROYED: c->uni->funcs->uni_output(c->uni, c->uni->arg, UNIAPI_PARTY_DESTROYED, cookie, msg); break; case SIGC_END: break; } return; drop: /* * This is for SAAL message signals that should be dropped. */ uni_msg_destroy(msg); UNI_FREE(u); } /**********************************************************************/ /* * Timeout functions */ static void t308_func(struct call *c) { uni_enq_call(c, SIGC_T308, 0, NULL, NULL); } static void t303_func(struct call *c) { uni_enq_call(c, SIGC_T303, 0, NULL, NULL); } static void t301_func(struct call *c) { uni_enq_call(c, SIGC_T301, 0, NULL, NULL); } static void t310_func(struct call *c) { uni_enq_call(c, SIGC_T310, 0, NULL, NULL); } static void t313_func(struct call *c) { uni_enq_call(c, SIGC_T313, 0, NULL, NULL); } static void t322_func(struct call *c) { uni_enq_call(c, SIGC_T322, 0, NULL, NULL); } /**********************************************************************/ /* * Check whether the peer state is compatible with our state. * Return the new callstate we should go to (either U0 or the current * state). * None of the state is U0 here. My state is not U11 or U12. * * Well, this turns out to be not so easy: the status enquiry could have * been sent before we changed into the current state - the status will * report a previous state without anything been lost. * * Incoming states are incompatible with outgoing states. Everything is ok. */ static enum call_state state_compat(struct call *c, enum uni_callstate peer) { if ((c->cstate == CALLST_U1 || c->cstate == CALLST_U3 || c->cstate == CALLST_U4) && (peer == UNI_CALLSTATE_N6 || peer == UNI_CALLSTATE_N7 || peer == UNI_CALLSTATE_N8 || peer == UNI_CALLSTATE_N9)) return (CALLST_NULL); if ((c->cstate == CALLST_N6 || c->cstate == CALLST_N7 || c->cstate == CALLST_N8 || c->cstate == CALLST_N9) && (peer == UNI_CALLSTATE_U1 || peer == UNI_CALLSTATE_U3 || peer == UNI_CALLSTATE_U4)) return (CALLST_NULL); if ((peer == UNI_CALLSTATE_N1 || peer == UNI_CALLSTATE_N3 || peer == UNI_CALLSTATE_N4) && (c->cstate == CALLST_U6 || c->cstate == CALLST_U7 || c->cstate == CALLST_U8 || c->cstate == CALLST_N9)) return (CALLST_NULL); if ((peer == UNI_CALLSTATE_U6 || peer == UNI_CALLSTATE_U7 || peer == UNI_CALLSTATE_U8 || peer == UNI_CALLSTATE_U9) && (c->cstate == CALLST_N1 || c->cstate == CALLST_N3 || c->cstate == CALLST_N4)) return (CALLST_NULL); return (c->cstate); }