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:
parent
d6f49749dc
commit
537e110011
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue