523 lines
13 KiB
C
523 lines
13 KiB
C
/* ITU-T Q.77x TCAP / CSL - Component Sub-Layer */
|
|
|
|
#include <errno.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include <osmocore/talloc.h>
|
|
#include <osmocore/msgb.h>
|
|
|
|
#include <asn1c/OBJECT_IDENTIFIER.h>
|
|
|
|
#include <osmocom/tcap/TCMessage.h>
|
|
#include <osmocom/tcap/ExternalPDU.h>
|
|
#include <osmocom/tcap/DialoguePDU.h>
|
|
#include <osmocom/tcap/UniDialoguePDU.h>
|
|
|
|
#include "tcap.h"
|
|
|
|
static uint8_t _dial_version1 = 0x80;
|
|
|
|
static BIT_STRING_t dial_version1 = {
|
|
.buf = &_dial_version1,
|
|
.size = 1,
|
|
.bits_unused = 7,
|
|
};
|
|
|
|
static ComponentPortion_t *assemble_components(ComponentPortion_t *cp, struct tcap_dialogue *td)
|
|
{
|
|
struct tcap_component *tcomp;
|
|
unsigned int comp_count = 0;
|
|
|
|
memset(cp, 0, sizeof(*cp));
|
|
llist_for_each_entry(tcomp, &td->pend_comp_list, list) {
|
|
ASN_SEQUENCE_ADD(&cp->list, &tcomp->comp);
|
|
comp_count++;
|
|
}
|
|
if (comp_count)
|
|
return cp;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* TC-UNI.req from TCU */
|
|
int tcap_csl_tc_uni_req(struct tcap_dialogue *td, OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info)
|
|
{
|
|
struct TCMessage tcm;
|
|
ExternalPDU_t ext;
|
|
UniDialoguePDU_t dial;
|
|
ANY_t any;
|
|
struct tcap_invocation *ti;
|
|
uint32_t trans_id;
|
|
int rc;
|
|
|
|
if (app_ctx) {
|
|
AUDT_apdu_t *audt;
|
|
/* build AUDT apdu */
|
|
memset(&dial, 0, sizeof(dial));
|
|
dial.present = UniDialoguePDU_PR_unidialoguePDU;
|
|
audt = &dial.choice.unidialoguePDU;
|
|
if (user_info)
|
|
audt->user_information = user_info;
|
|
memcpy(&audt->application_context_name, app_ctx, sizeof(audt->application_context_name));
|
|
audt->protocol_version = &dial_version1;
|
|
|
|
fprintf(stdout, "\nDialogue portion:\n");
|
|
xer_fprint(stdout, &asn_DEF_UniDialoguePDU, &dial);
|
|
|
|
memset(&ext, 0, sizeof(ext));
|
|
ANY_fromType(&ext.dialog, &asn_DEF_UniDialoguePDU, &dial);
|
|
memset(&any, 0, sizeof(any));
|
|
ANY_fromType(&any, &asn_DEF_ExternalPDU, &ext);
|
|
tcm.choice.unidirectional.dialoguePortion = (OCTET_STRING_t *) &any;
|
|
}
|
|
/* Request components to CHA */
|
|
/* Process components */
|
|
/* Assemble TSL user data */
|
|
assemble_components(&tcm.choice.unidirectional.components, td);
|
|
|
|
/* TR-UNI-REQ to TSL */
|
|
rc = tcap_tco_tr_uni_req(&td->trans, &tcm);
|
|
/* Dialogue terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
/* FIXME: Free Dialogue ID */
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* TC-BEGIN.req from TCU */
|
|
int tcap_csl_tc_begin_req(struct tcap_dialogue *td, OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info)
|
|
{
|
|
struct TCMessage tcm;
|
|
struct ComponentPortion cp;
|
|
ExternalPDU_t ext;
|
|
DialoguePDU_t dial;
|
|
ANY_t any;
|
|
struct tcap_invocation *ti;
|
|
uint32_t trans_id;
|
|
int rc;
|
|
|
|
memset(&tcm, 0, sizeof(tcm));
|
|
tcm.present = TCMessage_PR_begin;
|
|
|
|
if (app_ctx) {
|
|
AARQ_apdu_t *aarq;
|
|
|
|
memset(&dial, 0, sizeof(dial));
|
|
dial.present = DialoguePDU_PR_dialogueRequest;
|
|
|
|
aarq = &dial.choice.dialogueRequest;
|
|
if (user_info)
|
|
aarq->user_information = user_info;
|
|
/* Set Application context mode */
|
|
memcpy(&aarq->application_context_name, app_ctx, sizeof(aarq->application_context_name));
|
|
/* Set protocol_version = 1 */
|
|
aarq->protocol_version = &dial_version1;
|
|
/* Build AARQ apdu */
|
|
fprintf(stdout, "\nDialogue portion:\n");
|
|
xer_fprint(stdout, &asn_DEF_DialoguePDU, &dial);
|
|
|
|
memset(&ext, 0, sizeof(ext));
|
|
ANY_fromType(&ext.dialog, &asn_DEF_DialoguePDU, &dial);
|
|
memset(&any, 0, sizeof(any));
|
|
ANY_fromType(&any, &asn_DEF_ExternalPDU, &ext);
|
|
tcm.choice.begin.dialoguePortion = (OCTET_STRING_t *) &any;
|
|
}
|
|
/* Request components to CHA */
|
|
/* Process components */
|
|
/* Assemble TSL user data */
|
|
tcm.choice.begin.components = assemble_components(&cp, td);
|
|
|
|
/* Assign local transaction ID */
|
|
trans_id = ntohl(td->trans.tid_local);
|
|
OCTET_STRING_fromBuf(&tcm.choice.begin.otid,
|
|
(const char *) &trans_id, sizeof(trans_id));
|
|
|
|
/* TR-BEGIN-REQ to TSL */
|
|
rc = tcap_tco_tr_begin_req(&td->trans, &tcm);
|
|
|
|
td->trans.state = TCAP_TS_INIT_SENT;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* TR-UNI.ind from TSL */
|
|
int tcap_csl_tr_uni_ind(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
|
|
{
|
|
struct tcap_dialogue *td = dialg_by_trans(tt);
|
|
#if 0
|
|
if (app_ctx || user_info) {
|
|
/* extract dialogue portion */
|
|
/* Check for correctness */
|
|
/* Check version 1 */
|
|
}
|
|
/* Assign Dialogue ID */
|
|
td->dialogue_id = tcap_dialg_id_alloc();
|
|
/* TC-UNI.ind to TCU */
|
|
rc = tcap_tcu_uni_ind(td, app_ctx, user_info, comp_resent);
|
|
/* Components to CHA */
|
|
/* Dialogue Terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
/* Free Dialogue ID */
|
|
#endif
|
|
}
|
|
|
|
/* TR-BEGIN.ind from TSL(TSM) -> CSL(DHA) */
|
|
int tcap_csl_tr_begin_ind(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
|
|
{
|
|
struct tcap_dialogue *td = dialg_by_trans(tt);
|
|
struct Begin *bgmsg = &tcmsg->choice.begin;
|
|
OBJECT_IDENTIFIER_t *app_ctx = NULL;
|
|
void *user_info;
|
|
int rc = 0;
|
|
|
|
if (bgmsg->dialoguePortion) {
|
|
/* extract dialogue portion */
|
|
if (0/* Check for correctness */) {
|
|
/* Build ABORT apdu */
|
|
/* Discard components */
|
|
/* TR-U-ABPRT.req to TSL */
|
|
rc = tcap_tco_tr_u_abort_req(tt, tcmsg);
|
|
/* Dalogue terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
}
|
|
if (0/* Check version 1 */) {
|
|
/* Build AARE apdu */
|
|
/* Discard components */
|
|
/* TR-P-ABPRT.req to TSL */
|
|
/* Dalogue terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
}
|
|
/* FIXME: Set application context mode */
|
|
}
|
|
/* Assign Dialogue ID */
|
|
td->dialogue_id = tcap_dialg_id_alloc();
|
|
/* TC-BEGIN.ind to TCU */
|
|
rc = tcap_tcu_begin_ind(td, app_ctx, user_info, bgmsg->components ? 1 : 0);
|
|
if (bgmsg->components) {
|
|
rc = tcap_cha_proc_components(td, bgmsg->components);
|
|
/* Components to CHA */
|
|
}
|
|
tt->state = TCAP_TS_INIT_RECV;
|
|
}
|
|
|
|
static int gen_ext_AARE(struct DialoguePDU *dial, ANY_t *any, ExternalPDU_t *ext,
|
|
OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info)
|
|
{
|
|
AARE_apdu_t *aare;
|
|
int rc;
|
|
|
|
memset(&dial, 0, sizeof(dial));
|
|
dial->present = DialoguePDU_PR_dialogueResponse;
|
|
aare = &dial->choice.dialogueResponse;
|
|
/* Set protocol version 1 */
|
|
aare->protocol_version = &dial_version1;
|
|
/* Build AARE-apdu (accepted) */
|
|
memcpy(&aare->application_context_name, app_ctx, sizeof(aare->application_context_name));
|
|
asn_long2INTEGER(&aare->result, Associate_result_accepted);
|
|
aare->user_information = user_info;
|
|
/* Link Dialogue into External PDU */
|
|
rc = ANY_fromType(&ext->dialog, &asn_DEF_DialoguePDU, &dial);
|
|
if (rc < 0)
|
|
return rc;
|
|
/* Link External PDU into Dialogue Portion */
|
|
rc = ANY_fromType(any, &asn_DEF_ExternalPDU, &ext);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* TC-CONTINUE.req from TCU */
|
|
int tcap_csl_tc_cont_req(struct tcap_dialogue *td, OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info)
|
|
{
|
|
struct TCMessage tcm;
|
|
struct ComponentPortion cp;
|
|
ExternalPDU_t ext;
|
|
DialoguePDU_t dial;
|
|
ANY_t any;
|
|
uint32_t trans_id;
|
|
int rc = 0;
|
|
|
|
memset(&tcm, 0, sizeof(tcm));
|
|
tcm.present = TCMessage_PR_continue;
|
|
|
|
switch (td->trans.state) {
|
|
case TCAP_TS_INIT_RECV:
|
|
if (app_ctx && user_info) {
|
|
gen_ext_AARE(&dial, &any, &ext, app_ctx, user_info);
|
|
fprintf(stdout, "\nTC-CONTINUE.req Dialogue portion:\n");
|
|
xer_fprint(stdout, &asn_DEF_DialoguePDU, &dial);
|
|
tcm.choice.Continue.dialoguePortion = (OCTET_STRING_t *) &any;
|
|
}
|
|
break;
|
|
case TCAP_TS_ACTIVE:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "TC-CONTNUE.req in invalid state\n");
|
|
return -EINVAL;
|
|
}
|
|
/* Request components to CHA */
|
|
/* Process components */
|
|
/* Assemble TSL user data */
|
|
tcm.choice.Continue.components = assemble_components(&cp, td);
|
|
|
|
/* TR-CONTINUE to TSL */
|
|
rc = tcap_tco_tr_continue_req(&td->trans, &tcm);
|
|
|
|
td->trans.state = TCAP_TS_ACTIVE;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* TC-END.req from TCU */
|
|
int tcap_csl_tc_end_req(struct tcap_dialogue *td, OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info, int prearranged)
|
|
{
|
|
struct TCMessage tcm;
|
|
struct ComponentPortion cp;
|
|
ExternalPDU_t ext;
|
|
DialoguePDU_t dial;
|
|
ANY_t any;
|
|
uint32_t trans_id;
|
|
int rc = 0;
|
|
|
|
memset(&tcm, 0, sizeof(tcm));
|
|
tcm.present = TCMessage_PR_end;
|
|
|
|
switch (td->trans.state) {
|
|
case TCAP_TS_INIT_RECV:
|
|
case TCAP_TS_ACTIVE:
|
|
break;
|
|
case TCAP_TS_INIT_SENT:
|
|
/* TR-END.req to TSL */
|
|
rc = tcap_tco_tr_end_req(&td->trans, &tcm);
|
|
/* Dialogue terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
return rc;
|
|
default:
|
|
fprintf(stderr, "TC-END.req in invalid state\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (prearranged) {
|
|
/* TR-END.req to TSL */
|
|
rc = tcap_tco_tr_end_req(&td->trans, &tcm);
|
|
/* Dialogue terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
/* Free Dialogue ID */
|
|
return rc;
|
|
}
|
|
if (td->trans.state == TCAP_TS_INIT_RECV && (app_ctx && user_info)) {
|
|
gen_ext_AARE(&dial, &any, &ext, app_ctx, user_info);
|
|
fprintf(stdout, "\nTC-END.req Dialogue portion:\n");
|
|
xer_fprint(stdout, &asn_DEF_DialoguePDU, &dial);
|
|
tcm.choice.end.dialoguePortion = (OCTET_STRING_t *) &any;
|
|
}
|
|
/* Request component to CHA */
|
|
/* Process components */
|
|
/* Assemble TLS user data */
|
|
tcm.choice.end.components = assemble_components(&cp, td);
|
|
/* TR-END.req to TSL */
|
|
rc = tcap_tco_tr_end_req(&td->trans, &tcm);
|
|
/* Dialogue terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
/* FIXME: Free Dialogue ID */
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* TC-U-APORT.req from TCU */
|
|
int tcap_csl_tc_u_abort_req(struct tcap_dialogue *td, uint32_t *abrt_reason,
|
|
OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info)
|
|
{
|
|
struct tcap_transaction *tt = &td->trans;
|
|
void *app_ctx_mode;
|
|
|
|
switch (tt->state) {
|
|
case TCAP_TS_INIT_RECV:
|
|
case TCAP_TS_ACTIVE:
|
|
break;
|
|
case TCAP_TS_INIT_SENT:
|
|
app_ctx_mode = NULL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (app_ctx_mode) {
|
|
if (tt->state != TCAP_TS_ACTIVE && 0 /* Abort reason present && AC-name not supported || dialogure refused */) {
|
|
/* Set protocol version 1 */
|
|
/* Build AARE apdu (rejected) */
|
|
} else {
|
|
/* Build ABRT apdu (source = dialogue-service-user) */
|
|
}
|
|
}
|
|
/* TR-U-ABORT.req to TSL */
|
|
//tcap_tco_tr_u_abort_req();
|
|
/* Dialogue terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
/* Free Dialogue ID */
|
|
}
|
|
|
|
/* TR-END.ind from TSL */
|
|
int tcap_csl_tr_end_ind(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
|
|
{
|
|
struct tcap_dialogue *td = dialg_by_trans(tt);
|
|
struct End *endmsg = &tcmsg->choice.end;
|
|
void *app_ctx_name, *user_info;
|
|
int rc = 0;
|
|
int app_ctx_mode = 0;
|
|
|
|
switch (td->trans.state) {
|
|
case TCAP_TS_ACTIVE:
|
|
if (endmsg->dialoguePortion) {
|
|
/* FIXME: Discard components */
|
|
/* TC-P-ABORT.ind to TCU */
|
|
//rc = tcap_tcu_abort_ind(td, app_ctx_name, user_info);
|
|
} else {
|
|
/* TC-END.ind to TCU */
|
|
//rc = tcap_tcu_end_ind(td, app_ctx_name, user_info, components);
|
|
if (endmsg->components) {
|
|
/* Components to CHA */
|
|
}
|
|
}
|
|
/* Dialogue terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
/* Free Dialogue ID */
|
|
break;
|
|
case TCAP_TS_INIT_SENT:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (endmsg->dialoguePortion) {
|
|
if (app_ctx_mode) {
|
|
/* Extract dialogue portion */
|
|
/* Is dialogue portion corect? */
|
|
if (0 /*!correct */) {
|
|
/* TC-P-ABORT.ind to TCU */
|
|
rc = tcap_tcu_abort_ind(td, app_ctx_name, user_info);
|
|
}
|
|
/* TC-END.ind to TCU */
|
|
rc = tcap_tcu_end_ind(td, app_ctx_name, user_info, endmsg->components ? 1 : 0);
|
|
if (endmsg->components) {
|
|
/* Components to CHA */
|
|
}
|
|
}
|
|
} else {
|
|
if (app_ctx_mode) {
|
|
/* Discard components */
|
|
/* TC-P-ABORT.ind to TCU */
|
|
rc = tcap_tcu_abort_ind(td, app_ctx_name, user_info);
|
|
}
|
|
}
|
|
/* Dialogue terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
/* Free Dialogue ID */
|
|
}
|
|
|
|
/* TR-NOTICE.ind from TSL */
|
|
int tcap_csl_tr_notice_ind(struct tcap_transaction *tt)
|
|
{
|
|
struct tcap_dialogue *td = dialg_by_trans(tt);
|
|
uint32_t cause = 0;
|
|
|
|
switch (tt->state) {
|
|
case TCAP_TS_INIT_SENT:
|
|
case TCAP_TS_ACTIVE:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* TR-NOTICE.ind to TCU */
|
|
return tcap_tcu_notice_ind(td, cause);
|
|
}
|
|
|
|
/* TR-CONTINUE.ind from TSL */
|
|
int tcap_csl_tr_continue_ind(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
|
|
{
|
|
struct tcap_dialogue *td = dialg_by_trans(tt);
|
|
struct Continue *ctmsg = &tcmsg->choice.Continue;
|
|
int rc = 0;
|
|
int app_ctx_mode = 0;
|
|
void *app_ctx_name, *user_info;
|
|
|
|
switch (tt->state) {
|
|
case TCAP_TS_INIT_SENT:
|
|
break;
|
|
/* FIXME: TCAP_TS_ACTIVE */
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ctmsg->dialoguePortion) {
|
|
if (app_ctx_mode) {
|
|
/* Extract dialogue portion */
|
|
if (0 /*!correct*/) {
|
|
err_discard:
|
|
/* Discard components */
|
|
/* TC-P-ABORT.ind to TCU */
|
|
rc = tcap_tcu_abort_ind(td, app_ctx_name, user_info);
|
|
/* Build ABRT apdu */
|
|
/* TR-U-ABORT.req to TSL */
|
|
rc = tcap_tco_tr_u_abort_req(tt, tcmsg);
|
|
/* Dialogue terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
/* Free Dialogue ID */
|
|
return rc;
|
|
}
|
|
} else
|
|
goto err_discard;
|
|
} else {
|
|
if (app_ctx_mode)
|
|
goto err_discard;
|
|
}
|
|
/* TC-CONTINUE.ind to TCU */
|
|
rc = tcap_tcu_cont_ind(td, app_ctx_name, user_info, ctmsg->components ? 1 : 0);
|
|
if (ctmsg->components) {
|
|
/* Components to CHA */
|
|
}
|
|
tt->state = TCAP_TS_ACTIVE;
|
|
return rc;
|
|
}
|
|
|
|
/* TR-U-ABORT.ind from TSL */
|
|
int tcap_csl_tr_u_abort_ind(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
|
|
{
|
|
struct tcap_dialogue *td = dialg_by_trans(tt);
|
|
|
|
switch (tt->state) {
|
|
case TCAP_TS_INIT_SENT:
|
|
break;
|
|
/* FIXME: TCAP_TS_ACTIVE */
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* FIXME */
|
|
}
|
|
|
|
/* TR-P-ABORT.ind from TSL */
|
|
int tcap_csl_tr_p_abort_ind(struct tcap_transaction *tt)
|
|
{
|
|
struct tcap_dialogue *td = dialg_by_trans(tt);
|
|
void *app_ctx_name, *user_data;
|
|
int rc;
|
|
|
|
switch (tt->state) {
|
|
case TCAP_TS_INIT_SENT:
|
|
case TCAP_TS_ACTIVE:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* TC-P-ABORT.ind to TCU */
|
|
rc = tcap_tcu_abort_ind(td, app_ctx_name, user_data);
|
|
/* Dialog terminated to CHA */
|
|
tcap_cha_dialg_term(td);
|
|
/* Free Dialogue ID */
|
|
}
|
|
|