From 537e110011473aaf4234910f787fa2e0235e4aba Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 22 Jul 2010 22:22:17 +0200 Subject: Introduce new (unfinished) application support layer This will be what applicaitons like the MSC or SGSN actually use to invoke MAP operations. --- src/app_support.c | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 src/app_support.c diff --git a/src/app_support.c b/src/app_support.c new file mode 100644 index 0000000..bf2cab8 --- /dev/null +++ b/src/app_support.c @@ -0,0 +1,286 @@ +/* TCAP/MAP application support code */ + +/* (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 + +#include "tcap_user.h" + +static LLIST_HEAD(map_app_list); + +/* One suppoerted application context */ +struct map_supp_app_ctx { + struct tcap_obj_ident obj_id; +}; + +struct map_op_type_info { + /* The localValue of the Operation */ + uint32_t local_op_code; + /* ASN type descriptor for the user_data argument of INVOKE */ + asn_TYPE_descriptor_t *inv_type; + /* ASN type descriptor for the user_data argument of RESULT */ + asn_TYPE_descriptor_t *res_type; +}; + +/* Structure that an application can register */ +struct map_app_reg_info { + struct llist_head list; + + struct map_supp_ap_ctx *supp_app_ctx; + unsigned int num_supp_app_ctx; + + struct map_op_type_info *op_type_info; + unsigned int num_op_type_info; + + /* queue for incoming (received) primitives from the network */ + struct llist_head prim_upqueue; + + /* callbcak for COMPONENT related primitives (indication) */ + //int (*comp_ind_cb)( +}; + +/* local opCode, pointer to parsed asn1C structure, tcap indication, private ptr */ +typedef void map_cbfn(long opcode, void *parsed, struct tcap_component_ind *tcci, void *priv); + +struct map_op_callback { + void *priv; + /* call-back function to be called once the operation completes/fails/time-outs*/ + map_cbfn *cb; +}; + +int map_call(long opcode, void *parsed, void *priv, map_cbfn *cb); + +/* FIXME: auto-generate this from the ASN.1 specs */ +extern const struct map_op_type_info map_std_op_info[] = { + { 2, &asn_DEF_UpdateGprsLocationArg, &asn_DEF_UpdateGprsLocationRes }, + { 3, &asn_DEF_CancelLocationArg, &asn_DEF_CancelLocationRes }, + { 23, &asn_DEF_UpdateGprsLocationArg, &asn_DEF_UpdateGprsLocationRes }, + { 7, &asn_DEF_InsertSubscriberDataArg, &asn_DEF_InsertSubscriberDataRes }, + { 8, &asn_DEF_DeleteSubscriberDataArg, &asn_DEF_DeleteSubscriberDataRes }, + { 56, &asn_DEF_SendAuthenticationInfoArg, &asn_DEF_SendAuthenticationInfoRes }, + /* FIXME: complete this! */ +}; + +static const struct map_op_type_info * +get_op_type_info(struct map_app_reg_info *app, uint32_t local_op) +{ + unsigned int i; + struct map_op_type_info *optinf; + + for (i = 0; i < app->num_op_type_info; i++) { + optinf = &app->op_type_info[i]; + if (optinf->local_op_code == local_op) + return optinf; + } + + return NULL; +} + +int map_app_register(struct map_app_reg_info *mi) +{ + llist_add(&mi->list, &map_app_list); +} + +/* The User Application is initiating a new MAP operation */ +int map_op_invoke(long opcode, void *parsed, struct map_op_callback *cb, + uint32_t timeout_sec, uint32_t dialogue_id, uint8_t *linked_id) +{ + struct map_op_type_info *optinf; + struct tcap_prim_buf *tcpb = &_tcpb; + struct tcap_component_ind *tcci = &tcpb->comp; + + memset(&_tcpb, 0, sizeof(_tcpb)); + + optinf = get_op_type_info(app, opcode); + if (!optinf) + return -EINVAL; + + tcpb->prim = TCAP_PR_TC_INVOKE; + tcpb->user_ref = (unsigned long) cb; + tcci->op_class = 1; /* do we need other types in MAP? */ + tcci->dialg_id = dialogue_id; + /* we currently only do one invocation per dialogue */ + tcci->invoke_id = 1; + if (linked_id) { + tcci->_linked_id = *linked_id; + tcci->linked_id = &tcci->_linked_id; + } + tcci->operation.local = opcode; + + if (parsed) { + asn_enc_rval_t er; + er = der_encode_to_buffer(optinf->inv_type, parsed, + tcci->parameter.data, + tcci->parameter.data_len); + if (er.encoded < 0) + return -EINVAL; + } + + return tcap_user_req(tcpb); +} + +/* process an incoming TCAP component indication from the TCAP stack */ +static int process_tcap_comp_ind(struct tcap_prim_buf *tcpb) +{ + /* component primitives */ + struct tcap_component_ind *tcci = &tcpb->comp; + struct map_op_type_info *optinf; + void *parsed = NULL; + asn_dec_rval_t rv; + int rc = 0; + + /* We only support local operation codes for now! */ + if (tcci->operation.is_global) + goto out_reject; + + /* find out which ASN.1 types to expect in the component parameter */ + optinf = get_op_type_info(app, tcci->operation.local); + if (!optinf) + goto out_reject; + + switch (tcpb->prim) { + case TC_PR_TC_INVOKE: + /* parse user information */ + rv = ber_decode(NULL, optinf->inv_type, &parsed, + tcci->parameter.data, + tcci->parameter.data_len); + if (rv.code != RC_OK) { + rc = -EIO; + goto out_reject; + } + rc = send_map_primitive(MAP_PR_COMPONENT, tcpb); + break; + case TC_RR_TC_RESULT_L: + case TC_RR_TC_RESULT_NL: + /* parse user information */ + rv = ber_decode(NULL, optinf->res_type, &parsed, + tcci->parameter.data, + tcci->parameter.data_len); + if (rv.code != RC_OK) { + rc = -EIO; + goto out_reject; + } + /* Resolve and call the invocation specific completion + * callback */ + if (!tcci->user_ref) { + rc = -EIO; + goto out_reject; + } + cb = (struct map_op_callback *) tcci->user_ref; + if (!cb->cbfn) { + rc = -EIO; + goto out_reject; + } + cb->cbfn(FIXME, parsed, tcpb, priv); + break; + case TCAP_PR_TC_U_ERROR: + case TCAP_PR_TC_U_REJECT: + case TCAP_PR_TC_L_REJECT: + case TCAP_PR_TC_R_REJECT: + case TCAP_PR_TC_P_ABORT: + /* FIXME */ + default: + fprintf(stderr, "Unsupported/Unknown TCAP Component Primitive %s\n", + tcap_prim_name(tcpb->prim)); + } + + /* Free the parsed information */ + talloc_free(parsed); + + /* check if this was the last component and generate + * MAP-DELIMITER.ind */ + if (tcpb->comp.last_component) + rc = send_map_primitive(MAP_PR_DELIMITER_IND, ); + + return rc; + +out_reject: + /* Free the parsed information */ + talloc_free(parsed); + + /* FIXME */ + + return rc; +} + + +/* Process an incoming Dialogue indication from TCAP stack */ +static int process_tcap_dialg_ind(struct tcap_prim_buf *tcpb) +{ + struct tcap_dialogue_ind *tcdi = &tcpb->dialg; + int rc; + + switch (tcpb->prim) { + case TCAP_PR_TC_BEGIN: + /* FIXME: parse asn_DEF_MAP_OpenInfo */ + rc = send_map_primitive(MAP_PR_OPEN_IND, ); + break; + case TCAP_PR_TC_CONTINUE: + if (tcdi->app_ctx_present) { + /* FIXME: parse asn_DEF_MAP_OpenInfo */ + send_map_primitive(MAP_PR_OPEN_RESP, ); + } + break; + case TCAP_PR_TC_END: + send_map_primitive(MAP_PR_CLOSE_IND, ); + break; + case TCAP_PR_TC_U_ABORT: + case TCAP_PR_TC_NOTICE: + default: + fprintf(stderr, "Unsupported/Unknown TCAP Dialogue Primitive %s\n", + tcap_prim_name(tcpb->prim)); + rc = -EINVAL; + break; + } + + return rc; +} + +/* Process an incoming TCAP primitive from TCAP stack */ +static int process_tcap_ind(struct tcap_prim_buf *tcpb) +{ + int rc; + struct map_op_callback *cb; + + /* Hand it off to the respective sub-function + * for dialogue or component indications */ + if (tcpb->prim > _TCAP_PR_COMP_BASE) + rc = process_tcap_comp_ind(tcpb); + else + rc = process_tcap_dialg_ind(tcpb); + + /* Free the TCAP primitive */ + talloc_free(tcpb); + + return rc; +} + +/* callback for incoming primitives form TCAP */ +int tcap_user_ind_cb(struct tcap_prim_buf *tcpb) +{ + /* FIXME: Later we may want to simply enqueue the tcpb + * and have multiple threads pull from that queue */ + + return process_tcap_ind(tcpb); +} -- cgit v1.2.3