libosmo-tcap/src/csl_dha.c

580 lines
15 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, "\nTC-UNI.req Dialogue 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);
/* Free Dialogue ID */
tcap_dialg_free(td);
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, "\nTC-BEGIN.req Dialogue 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);
struct Unidirectional *unimsg = &tcmsg->choice.unidirectional;
OBJECT_IDENTIFIER_t *app_ctx = NULL;
void *user_info;
int rc = 0;
if (unimsg->dialoguePortion) {
/* 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, 1);
/* Components to CHA */
rc = tcap_cha_proc_components(td, &unimsg->components);
/* Dialogue Terminated to CHA */
tcap_cha_dialg_term(td);
/* Free Dialogue ID */
tcap_dialg_free(td);
return rc;
}
/* 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;
struct DialoguePDU *dial_pdu = NULL;
OBJECT_IDENTIFIER_t *app_ctx = NULL;
struct user_information *user_info = NULL;
int rc = 0;
if (bgmsg->dialoguePortion) {
struct ExternalPDU *ext_pdu = NULL;
asn_dec_rval_t adr;
struct AARQ_apdu *aarq;
/* extract dialogue portion */
adr = ber_decode(NULL, &asn_DEF_ExternalPDU, &ext_pdu,
bgmsg->dialoguePortion->buf,
bgmsg->dialoguePortion->size);
if (adr.code != RC_OK) {
fprintf(stderr, "TC-BEGIN.ind Error parsing Dialogue portion 1\n");
return -EINVAL;
}
adr = ber_decode(NULL, &asn_DEF_DialoguePDU, &dial_pdu,
ext_pdu->dialog.buf, ext_pdu->dialog.size);
/* Release the External part */
asn_DEF_ExternalPDU.free_struct(&asn_DEF_ExternalPDU, ext_pdu, 0);
/* Check for correctness */
if (adr.code != RC_OK ){ //||
//(dial_pdu && dial_pdu->present != DialoguePDU_PR_dialogueRequest)) {
fprintf(stderr, "TC-BEGIN.ind Error parsing Dialogue portion\n");
/* 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);
return rc;
}
fprintf(stdout, "\nTC-BEGIN.ind Dialogue portion:\n");
xer_fprint(stdout, &asn_DEF_UniDialoguePDU, dial_pdu);
aarq = &dial_pdu->choice.dialogueRequest;
if (aarq->protocol_version && 0/* Check version 1 */) {
/* Build AARE apdu */
/* Discard components */
/* TR-P-ABPRT.req to TSL */
/* Dalogue terminated to CHA */
tcap_cha_dialg_term(td);
}
/* Set application context mode */
app_ctx = &aarq->application_context_name;
user_info = aarq->user_information;
}
/* 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) {
/* Components to CHA */
rc = tcap_cha_proc_components(td, bgmsg->components);
}
tt->state = TCAP_TS_INIT_RECV;
if (dial_pdu) {
/* Release resources of the ber_decode */
asn_DEF_DialoguePDU.free_struct(&asn_DEF_DialoguePDU, dial_pdu, 0);
}
return rc;
}
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 */
tcap_dialg_free(td);
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);
/* Free Dialogue ID */
tcap_dialg_free(td);
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 */
tcap_dialg_free(td);
return 0;
}
/* 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 */
rc = tcap_cha_proc_components(td, endmsg->components);
}
}
/* Dialogue terminated to CHA */
tcap_cha_dialg_term(td);
/* Free Dialogue ID */
tcap_dialg_free(td);
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 */
rc = tcap_cha_proc_components(td, endmsg->components);
}
}
} 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 */
tcap_dialg_free(td);
return 0;
}
/* 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 */
tcap_dialg_free(td);
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 */
rc = tcap_cha_proc_components(td, ctmsg->components);
}
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 */
tcap_dialg_free(td);
return rc;
}