/* ITU-T Q.77x TCAP / ISM - Invocation State Machine, * part of CHA (ComponentHAndling), part of CSL (Component Sub-Layer) */ /* (C) 2010 by Harald Welte * (C) 2010 by On-Waves * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "tcap.h" static void tcap_ism_set_state(struct tcap_invocation *ti, enum tcap_invocation_state st) { printf("Invocation %d old_state=%s, new_state=%s\n", ti->invoke_id, tcap_inv_state_name(ti->state), tcap_inv_state_name(st)); ti->state = st; } struct tcap_invocation *tcap_ism_lookup(struct tcap_dialogue *td, int8_t invoke_id) { struct tcap_invocation *ti; llist_for_each_entry(ti, &td->ism_list, list) { if (ti->invoke_id == invoke_id) return ti; } return NULL; } struct tcap_invocation *tcap_ism_alloc(struct tcap_dialogue *td, int8_t invoke_id) { struct tcap_invocation *ti = talloc_zero(td, struct tcap_invocation); if (!ti) return NULL; ti->invoke_id = invoke_id; ti->linked_id = NULL; ti->dialogue = td; tcap_ism_set_state(ti, TCAP_IS_IDLE); llist_add_tail(&ti->list, &td->ism_list); return ti; } void tcap_ism_free(struct tcap_invocation *ti) { llist_del(&ti->list); talloc_free(ti); } /* Invocation timer expiry */ static void tcap_ism_inv_timer_exp(void *_ti) { struct tcap_invocation *ti = _ti; fprintf(stdout, "ISM Invoke Timer expired for InvokeID=%d\n", ti->invoke_id); switch (ti->state) { case TCAP_IS_OP_SENT_CL1: case TCAP_IS_OP_SENT_CL2: case TCAP_IS_OP_SENT_CL3: case TCAP_IS_OP_SENT_CL4: /* TC-L-CANCEL.ind (TCU <- CHA) */ tcap_ism_set_state(ti, TCAP_IS_INVALID); tcap_ism_free(ti); break; default: break; } } /* Reject timer expiry */ static void tcap_ism_rej_timer_exp(void *_ti) { struct tcap_invocation *ti = _ti; fprintf(stdout, "ISM Reject Timer expired for InvokeID=%d\n", ti->invoke_id); if (ti->state != TCAP_IS_WAIT_REJECT) return; tcap_ism_set_state(ti, TCAP_IS_INVALID); tcap_ism_free(ti); } /* Operation sent (CCO -> ISM) */ int tcap_ism_op_sent(struct tcap_invocation *ti) { /* Start invocation timer */ ti->inv_timer.cb = tcap_ism_inv_timer_exp; ti->inv_timer.data = ti; bsc_schedule_timer(&ti->inv_timer, ti->inv_timeout, 0); switch (ti->op_class) { case 1: tcap_ism_set_state(ti, TCAP_IS_OP_SENT_CL1); break; case 2: tcap_ism_set_state(ti, TCAP_IS_OP_SENT_CL2); break; case 3: tcap_ism_set_state(ti, TCAP_IS_OP_SENT_CL3); break; case 4: tcap_ism_set_state(ti, TCAP_IS_OP_SENT_CL4); break; default: return -EINVAL; } } /* RR-L received (CCO -> ISM) */ int tcap_ism_rr_l_recv(struct tcap_invocation *ti, struct ReturnResult *res, int last_component) { int rc = 0; OPERATION_t *opCode = NULL; Parameter_t *param = NULL; if (res->resultretres) { opCode = &res->resultretres->opCode; param = res->resultretres->parameter; } switch (ti->state) { case TCAP_IS_OP_SENT_CL1: case TCAP_IS_OP_SENT_CL3: /* TC-RESULT-L.ind (TCU <- CHA) */ rc = tcap_tcu_result_l_ind(ti, opCode, param, last_component); /* Stop invocation timer */ bsc_del_timer(&ti->inv_timer); /* Start reject timer */ ti->rej_timer.cb = tcap_ism_rej_timer_exp; ti->rej_timer.data = ti; bsc_schedule_timer(&ti->rej_timer, ti->rej_timeout, 0); tcap_ism_set_state(ti, TCAP_IS_WAIT_REJECT); break; case TCAP_IS_OP_SENT_CL2: case TCAP_IS_OP_SENT_CL4: /* Generate RJ component (CCO <- ISM) */ rc = tcap_cco_gen_rej(ti); /* Stop invocation timer */ bsc_del_timer(&ti->inv_timer); tcap_ism_set_state(ti, TCAP_IS_INVALID); tcap_ism_free(ti); break; } } /* RE received (CCO -> ISM) */ int tcap_ism_re_recv(struct tcap_invocation *ti, struct ReturnError *re, int last_component) { switch (ti->state) { case TCAP_IS_OP_SENT_CL1: case TCAP_IS_OP_SENT_CL2: /* TC-U-ERROR.ind (TCU <- CHA) */ /* Stop invocation timer */ bsc_del_timer(&ti->inv_timer); /* Start reject timer */ ti->rej_timer.cb = tcap_ism_rej_timer_exp; ti->rej_timer.data = ti; bsc_schedule_timer(&ti->rej_timer, ti->rej_timeout, 0); tcap_ism_set_state(ti, TCAP_IS_WAIT_REJECT); break; case TCAP_IS_OP_SENT_CL3: case TCAP_IS_OP_SENT_CL4: tcap_ism_set_state(ti, TCAP_IS_INVALID); tcap_ism_free(ti); break; default: return -EINVAL; } return 0; } /* RR-NL received (CCO -> ISM) */ int tcap_ism_rr_nl_recv(struct tcap_invocation *ti, struct ReturnResult *res, int last_component) { int rc = 0; OPERATION_t *opCode = NULL; Parameter_t *param = NULL; if (res->resultretres) { opCode = &res->resultretres->opCode; param = res->resultretres->parameter; } switch (ti->state) { case TCAP_IS_OP_SENT_CL1: case TCAP_IS_OP_SENT_CL3: /* TC-RESULT-NL.ind (TCU <- CHA) */ rc = tcap_tcu_result_nl_ind(ti, opCode, param, last_component); /* stay in SENT_CL1 state */ break; case TCAP_IS_OP_SENT_CL2: case TCAP_IS_OP_SENT_CL4: /* Generate RJ component (CCO <- ISM) */ rc = tcap_cco_gen_rej(ti); /* Stop invocation timer */ bsc_del_timer(&ti->inv_timer); tcap_ism_set_state(ti, TCAP_IS_INVALID); tcap_ism_free(ti); break; default: return -EINVAL; } return rc; } /* Terminate (CCO -> ISM) */ int tcap_ism_terminate(struct tcap_invocation *ti) { switch (ti->state) { case TCAP_IS_OP_SENT_CL1: case TCAP_IS_OP_SENT_CL2: case TCAP_IS_OP_SENT_CL3: case TCAP_IS_OP_SENT_CL4: case TCAP_IS_WAIT_REJECT: break; default: return -EINVAL; } /* Stop invoation timer */ bsc_del_timer(&ti->inv_timer); tcap_ism_set_state(ti, TCAP_IS_INVALID); tcap_ism_free(ti); return 0; }