Introduce new (unfinished) application support layer

This will be what applicaitons like the MSC or SGSN actually use to
invoke MAP operations.
This commit is contained in:
Harald Welte 2010-07-22 22:22:17 +02:00
parent d6f49749dc
commit 537e110011
1 changed files with 286 additions and 0 deletions

286
src/app_support.c Normal file
View File

@ -0,0 +1,286 @@
/* TCAP/MAP application support code */
/* (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 <libosmocore/linuxlist.h>
#include <libosmocore/msgb.h>
#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);
}