libosmo-tcap/src/tcu.c

426 lines
12 KiB
C

/* TC-User API / Interface between TCAP protocol manager and User Application */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (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 <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <errno.h>
#include <osmocore/msgb.h>
#include <osmocore/utils.h>
#include <osmocore/talloc.h>
#include <osmocom/tcap/Parameter.h>
#include <osmocom/tcap/OPERATION.h>
#include "tcap.h"
#include "tcap_user.h"
void *tcap_ind_ctx;
static const struct value_string tcap_prim_names[] = {
/* dialogue handling */
{ TCAP_PR_TC_UNI, "TC-UNI" },
{ TCAP_PR_TC_BEGIN, "TC-BEGIN" },
{ TCAP_PR_TC_CONTINUE, "TC-CONTINUE" },
{ TCAP_PR_TC_END, "TC-END" },
{ TCAP_PR_TC_U_ABORT, "TC-U-ABORT" },
{ TCAP_PR_TC_NOTICE, "TC-NOTICE" },
/* component handling */
{ TCAP_PR_TC_INVOKE, "TC-INVOKE" },
{ TCAP_PR_TC_RESULT_L, "TC-RESULT-L" },
{ TCAP_PR_TC_RESULT_NL, "TC-RESULT-NL" },
{ TCAP_PR_TC_U_ERROR, "TC-U-ERROR" },
{ TCAP_PR_TC_U_REJECT, "TC-U-REJECT" },
{ TCAP_PR_TC_CANCEL, "TC-CANCEL" },
{ TCAP_PR_TC_L_REJECT, "TC-L-REJECT" },
{ TCAP_PR_TC_R_REJECT, "TC-R-REJECT" },
{ TCAP_PR_TC_P_ABORT, "TC-P-ABORT" },
{ 0, NULL }
};
LIB_EXPORTED const char *tcap_prim_name(enum tcap_primitive prim)
{
return get_value_string(tcap_prim_names, prim);
}
/***********************************************************************/
/* Dialogue Primitives */
/***********************************************************************/
static struct tcap_dialg_ind *tcap_dialg_ind_alloc(void)
{
struct tcap_dialg_ind *tcdi = talloc_zero(tcap_ind_ctx,
struct tcap_dialg_ind);
return tcdi;
}
/* fill the application context and user information part of 'tcap_dialg_ind' */
static int fill_tcap_dialg_ind(struct tcap_dialg_ind *tcdi,
OBJECT_IDENTIFIER_t *app_ctx_name,
struct user_information *user_info)
{
int rc;
if (app_ctx_name) {
/* Parse the Application Context from an OID into our array of ARCs */
rc = OBJECT_IDENTIFIER_get_arcs(app_ctx_name, &tcdi->app_ctx_name.arc,
sizeof(tcdi->app_ctx_name.arc[0]),
ARRAY_SIZE(tcdi->app_ctx_name.arc));
if (rc < 0)
return rc;
tcdi->app_ctx_name.num_arcs = rc;
tcdi->app_ctx_present = 1;
}
/* copy the user_info */
if (user_info) {
EXTERNAL_t *ext;
if (user_info->list.count != 1)
return -EIO;
ext = user_info->list.array[0];
if (ext->size > sizeof(tcdi->user_info.data))
return -EIO;
memcpy(tcdi->user_info.data, ext->buf, ext->size);
tcdi->user_info.data_len = ext->size;
}
return 0;
}
/* Take any TC-Dialogue.ind from the stack and pass it to the user */
static int _tcap_tcu_dialg_ind(enum tcap_primitive prim, struct tcap_dialogue *td,
OBJECT_IDENTIFIER_t *app_ctx_name,
struct user_information *user_info, int comp_present)
{
struct tcap_dialg_ind *tcdi = tcap_dialg_ind_alloc();
int rc;
if (!tcdi)
return -ENOMEM;
tcdi->dialg_id = td->dialogue_id;
rc = fill_tcap_dialg_ind(tcdi, app_ctx_name, user_info);
if (rc < 0) {
/* FIXME: reject the dialogue */
fprintf(stderr, "Error filling the Dialogue Indication\n");
return rc;
}
if (comp_present)
tcdi->components_present = 1;
return tcap_user_ind_dialg(prim, tcdi);
}
/* TC-BEGIN.ind from DHA */
int tcap_tcu_begin_ind(struct tcap_dialogue *td, void *app_ctx_name, void *user_info, int comp_present)
{
return _tcap_tcu_dialg_ind(TCAP_PR_TC_BEGIN, td, app_ctx_name, user_info, comp_present);
}
/* TC-UNI.ind from DHA */
int tcap_tcu_uni_ind(struct tcap_dialogue *td, void *app_ctx_name, void *user_info, int comp_present)
{
return _tcap_tcu_dialg_ind(TCAP_PR_TC_UNI, td, app_ctx_name, user_info, comp_present);
}
/* TC-CONT.ind from DHA */
int tcap_tcu_cont_ind(struct tcap_dialogue *td, void *app_ctx_name, void *user_info, int comp_present)
{
return _tcap_tcu_dialg_ind(TCAP_PR_TC_CONTINUE, td, app_ctx_name, user_info, comp_present);
}
/* TC-END.ind from DHA */
int tcap_tcu_end_ind(struct tcap_dialogue *td, void *app_ctx_name, void *user_info, int comp_present)
{
return _tcap_tcu_dialg_ind(TCAP_PR_TC_END, td, app_ctx_name, user_info, comp_present);
}
/* TC-ABORT.ind from DHA */
int tcap_tcu_abort_ind(struct tcap_dialogue *td, void *app_ctx_name, void *user_info)
{
}
/* TC-NOTICE.ind from DHA */
int tcap_tcu_notice_ind(struct tcap_dialogue *td, uint32_t cause)
{
}
/* DIALOGUE primitive received from TC-User */
LIB_EXPORTED int tcap_user_req_dialg(enum tcap_primitive prim, struct tcap_dialg_ind *tcdi)
{
/* Allocate some structures on the stack */
OBJECT_IDENTIFIER_t *app_ctx;
EXTERNAL_t *ext;
struct user_information *user_info;
struct tcap_dialogue *td;
int rc = 0;
fprintf(stdout, "<- USER_REQ_DIALG(%s)\n", tcap_prim_name(prim));
/* Resolve (or allocate) the dialogue/transaction state */
td = tcap_dialg_by_dialg_id(tcdi->dialg_id);
if (!td) {
switch (prim) {
case TCAP_PR_TC_BEGIN:
td = tcap_dialg_alloc(tcdi->dialg_id);
if (!td)
return -EINVAL;
break;
default:
return -EINVAL;
}
}
/* Parse the tcap_dialg_ind into something that the asn1c code understands */
if (tcdi->app_ctx_present) {
app_ctx = talloc_zero(td, OBJECT_IDENTIFIER_t);
OBJECT_IDENTIFIER_set_arcs(app_ctx, &tcdi->app_ctx_name.arc,
sizeof(tcdi->app_ctx_name.arc[0]),
tcdi->app_ctx_name.num_arcs);
}
/* Parse the tcap_dialg_ind into something that the asn1c code understands */
if (tcdi->user_info_present) {
ext = talloc_zero(td, EXTERNAL_t);
ext->buf = tcdi->user_info.data;
ext->size = tcdi->user_info.data_len;
user_info = talloc_zero(ext, struct user_information);
ASN_SEQUENCE_ADD(&user_info->list, ext);
}
switch (prim) {
case TCAP_PR_TC_UNI:
rc = tcap_csl_tc_uni_req(td, app_ctx, user_info);
break;
case TCAP_PR_TC_BEGIN:
td->transp_ent = tcdi->transp_ent;
rc = tcap_csl_tc_begin_req(td, app_ctx, user_info);
break;
case TCAP_PR_TC_CONTINUE:
rc = tcap_csl_tc_cont_req(td, app_ctx, user_info);
break;
case TCAP_PR_TC_END:
rc = tcap_csl_tc_end_req(td, app_ctx, user_info, tcdi->prearranged_end);
break;
case TCAP_PR_TC_U_ABORT:
rc = tcap_csl_tc_u_abort_req(td, &tcdi->reason, app_ctx, user_info);
break;
default:
fprintf(stderr, "unsupported component primitive %s\n", tcap_prim_name(prim));
return -EINVAL;
}
return rc;
}
/***********************************************************************/
/* Component Primitives */
/***********************************************************************/
static struct tcap_component_ind *tcap_comp_ind_alloc(void)
{
struct tcap_component_ind *tcci = talloc_zero(tcap_ind_ctx,
struct tcap_component_ind);
return tcci;
}
static int _tcu_comp_ind(enum tcap_primitive prim, struct tcap_invocation *ti, struct OPERATION *oper,
Parameter_t *param, int last)
{
struct tcap_component_ind *tcci = tcap_comp_ind_alloc();
int rc;
tcci->dialg_id = ti->dialogue->dialogue_id;
tcci->invoke_id = ti->invoke_id;
if (ti->linked_id) {
tcci->_linked_id = ti->_linked_id;
tcci->linked_id = &tcci->_linked_id;
}
if (oper && oper->present != OPERATION_PR_NOTHING) {
switch (oper->present) {
case OPERATION_PR_localValue:
rc = asn_INTEGER2long(&oper->choice.localValue, &tcci->operation.local);
if (rc < 0)
goto out_free;
break;
case OPERATION_PR_globalValue:
rc = OBJECT_IDENTIFIER_get_arcs(&oper->choice.globalValue,
&tcci->operation.global.arc,
sizeof(tcci->operation.global.arc[0]),
ARRAY_SIZE(tcci->operation.global.arc));
if (rc < 0)
goto out_free;
tcci->operation.global.num_arcs = rc;
break;
default:
break;
}
}
if (param) {
if (param->size > sizeof(tcci->parameter.data))
goto out_free;
memcpy(tcci->parameter.data, param->buf, param->size);
tcci->parameter.data_len = param->size;
}
tcci->last_component = last;
return tcap_user_ind_comp(prim, tcci);
out_free:
talloc_free(tcci);
return rc;
}
/* Table 10 / Q.771 : TC-INVOKE.ind */
int tcap_tcu_tc_invoke_ind(struct tcap_invocation *ti, struct OPERATION *oper, Parameter_t *param, int last)
{
return _tcu_comp_ind(TCAP_PR_TC_INVOKE, ti, oper, param, last);
}
/* TC-L-REJECT.ind */
int tcap_tcu_tc_l_rej_ind(struct tcap_invocation *ti, int8_t *invoke_id, uint32_t problem)
{
/* FIXME */
}
/* TC-R-REJECT.ind */
int tcap_tcu_tc_r_rej_ind(struct tcap_invocation *ti, int8_t *invoke_id, uint32_t problem,
int last_component)
{
/* FIXME */
}
/* TC-RESULT-L.ind from ISM */
int tcap_tcu_result_l_ind(struct tcap_invocation *ti, struct OPERATION *oper, Parameter_t *param, int last)
{
return _tcu_comp_ind(TCAP_PR_TC_RESULT_L, ti, oper, param, last);
}
int tcap_tcu_result_nl_ind(struct tcap_invocation *ti, struct OPERATION *oper, Parameter_t *param, int last)
{
return _tcu_comp_ind(TCAP_PR_TC_RESULT_NL, ti, oper, param, last);
}
/* Allocate and Fill a OPERATION_t from information inside a tcap_component_ind */
static OPERATION_t *generate_op(struct tcap_dialogue *td, struct tcap_component_ind *tcci)
{
OPERATION_t *op;
op = talloc_zero(td, OPERATION_t);
if (!op)
return NULL;
if (tcci->operation.is_global) {
op->present = OPERATION_PR_globalValue;
OBJECT_IDENTIFIER_set_arcs(&op->choice.globalValue,
tcci->operation.global.arc,
sizeof(tcci->operation.global.arc[0]),
tcci->operation.global.num_arcs);
} else {
op->present = OPERATION_PR_localValue;
asn_long2INTEGER(&op->choice.localValue, tcci->operation.local);
}
return op;
}
/* Allocate and Fill an ErrorCode_t from information inside a tcap_component_ind */
static ErrorCode_t *generate_errcode(struct tcap_dialogue *td, struct tcap_component_ind *tcci)
{
ErrorCode_t *err;
err = talloc_zero(td, ErrorCode_t);
if (!err)
return NULL;
if (tcci->error.is_private) {
err->present = ErrorCode_PR_privateer;
asn_long2INTEGER(&err->choice.privateer, tcci->error.err);
} else {
err->present = ErrorCode_PR_nationaler;
err->choice.nationaler = tcci->error.err;
}
return err;
}
LIB_EXPORTED int tcap_user_req_comp(enum tcap_primitive prim, struct tcap_component_ind *tcci)
{
struct tcap_dialogue *td;
OPERATION_t *op = NULL;
ErrorCode_t *err = NULL;
int rc = 0;
fprintf(stdout, "<- USER_REQ_COMP(%s)\n", tcap_prim_name(prim));
/* Resolve (or allocate) the dialogue/transaction state */
td = tcap_dialg_by_dialg_id(tcci->dialg_id);
if (!td) {
switch (prim) {
case TCAP_PR_TC_INVOKE:
td = tcap_dialg_alloc(tcci->dialg_id);
td->trans.tid_local = tcap_trans_id_alloc();
break;
default:
return -EINVAL;
}
}
/* Actually dispatch the primitive */
switch (prim) {
case TCAP_PR_TC_INVOKE:
op = generate_op(td, tcci);
rc = tcap_cha_tc_invoke_req(td, tcci->op_class, tcci->invoke_id,
tcci->linked_id, op, tcci->parameter.data,
tcci->parameter.data_len, tcci->timeout_secs);
break;
case TCAP_PR_TC_RESULT_L:
op = generate_op(td, tcci);
rc = tcap_cha_tc_result_req(td, tcci->invoke_id, 1, op,
tcci->parameter.data, tcci->parameter.data_len);
break;
case TCAP_PR_TC_RESULT_NL:
op = generate_op(td, tcci);
rc = tcap_cha_tc_result_req(td, tcci->invoke_id, 0, op,
tcci->parameter.data, tcci->parameter.data_len);
break;
case TCAP_PR_TC_U_ERROR:
rc = tcap_cha_tc_u_error_req(td, tcci->invoke_id, generate_errcode(td, tcci),
tcci->parameter.data, tcci->parameter.data_len);
break;
case TCAP_PR_TC_U_REJECT:
rc = tcap_cha_tc_u_rej_req(td, &tcci->invoke_id, tcci->reject.problem_type,
tcci->reject.problem_code);
break;
case TCAP_PR_TC_CANCEL:
rc = tcap_cha_tc_cancel_req(td, tcci->invoke_id);
break;
case TCAP_PR_TC_TIMER_RESET:
default:
fprintf(stderr, "unsupported dialogue primitive %s\n", tcap_prim_name(prim));
return -EINVAL;
}
talloc_free(op);
talloc_free(err);
return rc;
}