libosmo-tcap/src/tsl_tsm.c

297 lines
6.7 KiB
C

/* ITU-T Q.77x TCAP / TSM - Transaction State Machine */
/* (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 <netinet/in.h>
#include <osmocore/msgb.h>
#include <osmocom/tcap/TCMessage.h>
#include "tcap.h"
#define TCAP_MSGB_ALLOC_SIZE 4096
#define TCAP_MSGB_HEADR_SIZE 128
static inline struct msgb *tcap_msgb_alloc(void)
{
return msgb_alloc_headroom(TCAP_MSGB_ALLOC_SIZE+TCAP_MSGB_HEADR_SIZE, TCAP_MSGB_HEADR_SIZE, "tcap");
}
/* BEGIN received from remote (TCO -> TSM) */
int tcap_tsm_begin_rcvd(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
{
struct Begin *msg_beg = &tcmsg->choice.begin;
int rc;
if (tt->state != TCAP_TS_IDLE)
return -EINVAL;
/* Store remote address and remote TID */
tt->tid_remote = ntohl(*(uint32_t *) msg_beg->otid.buf);
/* FIXME: DHA */
/* TR-BEGIN.ind to CSL */
rc = tcap_csl_tr_begin_ind(tt, tcmsg, msg);
tcap_trans_set_state(tt, TCAP_TS_INIT_RECV);
return rc;
}
/* BEGIN Transaction (TCO -> TSM) */
int tcap_tsm_begin_trans(struct tcap_transaction *tt, struct TCMessage *tcmsg)
{
struct tcap_dialogue *td = dialg_by_trans(tt);
struct msgb *msg;
asn_enc_rval_t ar;
int rc;
if (tt->state != TCAP_TS_IDLE)
return -EINVAL;
msg = tcap_msgb_alloc();
fprintf(stdout, "TC-BEGIN.req:\n");
xer_fprint(stdout, &asn_DEF_TCMessage, tcmsg);
/* FIXME: Store Local Address */
/* Assemble TR-portion of BEGIN message */
ar = der_encode_to_buffer(&asn_DEF_TCMessage, tcmsg, msg->data, msgb_tailroom(msg));
if (ar.encoded < 0) {
fprintf(stdout, "some error during encode\n");
msgb_free(msg);
return -EIO;
}
msgb_put(msg, ar.encoded);
/* Send N-UNITDATA.req to SCCP / SUA */
rc = tcap_scXp_n_unitdata_req(td->transp_ent, msg);
tcap_trans_set_state(tt, TCAP_TS_INIT_SENT);
return rc;
}
/* CONTINUE Transaction (TCO -> TSM) */
int tcap_tsm_continue_trans(struct tcap_transaction *tt, struct TCMessage *tcmsg)
{
struct tcap_dialogue *td = dialg_by_trans(tt);
enum tcap_transaction_state new_state;
struct msgb *msg;
asn_enc_rval_t ar;
int rc;
switch (tt->state) {
case TCAP_TS_INIT_RECV:
new_state = TCAP_TS_ACTIVE;
/* FIXME: Store new local address if provided by user */
break;
case TCAP_TS_ACTIVE:
new_state = TCAP_TS_ACTIVE;
break;
default:
return -EINVAL;
}
msg = tcap_msgb_alloc();
fprintf(stdout, "TC-CONTINUE.req:\n");
xer_fprint(stdout, &asn_DEF_TCMessage, tcmsg);
/* Assemble TR-portion of CONTINUE message */
ar = der_encode_to_buffer(&asn_DEF_TCMessage, tcmsg, msg->data, msgb_tailroom(msg));
if (ar.encoded < 0) {
fprintf(stdout, "some error during encode\n");
msgb_free(msg);
return -EIO;
}
msgb_put(msg, ar.encoded);
/* Send N-UNITDATA.req to SCCP / SUA */
rc = tcap_scXp_n_unitdata_req(td->transp_ent, msg);
tcap_trans_set_state(tt, new_state);
return rc;
}
/* END transaction (TCO -> TSM) */
int tcap_tsm_end_trans(struct tcap_transaction *tt, struct TCMessage *tcmsg)
{
struct tcap_dialogue *td = dialg_by_trans(tt);
struct msgb *msg;
int rc;
switch (tt->state) {
case TCAP_TS_INIT_RECV:
case TCAP_TS_ACTIVE:
if (1 /* FIXME: !prearranged */) {
/* FIXME: Assemble TR-portion of END message */
msg = tcap_msgb_alloc();
/* Send N-UNITDATA.req to SCCP / SUA */
rc = tcap_scXp_n_unitdata_req(td->transp_ent, msg);
}
break;
case TCAP_TS_INIT_SENT:
break;
default:
return -EINVAL;
}
/* TSM-stopped to TCO */
tcap_tco_tsm_stopped_ind(tt);
tcap_trans_set_state(tt, TCAP_TS_INVALID);
return 0;
}
/* ABORT transaction (TCO -> TSM) */
int tcap_tsm_abort_trans(struct tcap_transaction *tt, struct TCMessage *tcmsg)
{
struct tcap_dialogue *td = dialg_by_trans(tt);
struct msgb *msg;
int rc = 0;
switch (tt->state) {
case TCAP_TS_INIT_RECV:
case TCAP_TS_ACTIVE:
msg = tcap_msgb_alloc();
/* FIXME: Assemble TR-portion of ABORT message */
/* N-UNITDATA.req to SCCP / SUA */
rc = tcap_scXp_n_unitdata_req(td->transp_ent, msg);
break;
case TCAP_TS_INIT_SENT:
break;
default:
return -EINVAL;
}
/* TSM-stopped to TCO */
tcap_tco_tsm_stopped_ind(tt);
tcap_trans_set_state(tt, TCAP_TS_INVALID);
return rc;
}
/* CONTINUE received (TCO -> TSM) */
int tcap_tsm_continue_rcvd(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
{
enum tcap_transaction_state new_state;
struct Continue *msg_cnt = &tcmsg->choice.Continue;
int rc;
switch (tt->state) {
case TCAP_TS_INIT_SENT:
/* FIXME: Store remote address */
/* Store remote TID */
tt->tid_remote = ntohl(*(uint32_t *) msg_cnt->otid.buf);
/* fall-through */
case TCAP_TS_ACTIVE:
/* TR-CONTINUE.ind to CSL */
rc = tcap_csl_tr_continue_ind(tt, tcmsg, msg);
new_state = TCAP_TS_ACTIVE;
break;
default:
return -EINVAL;
}
tcap_trans_set_state(tt, new_state);
return rc;
}
/* END received (TCO -> TSM) */
int tcap_tsm_end_rcvd(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
{
int rc;
switch (tt->state) {
case TCAP_TS_INIT_SENT:
case TCAP_TS_ACTIVE:
break;
default:
return -EINVAL;
}
/* TR-END.ind to CSL */
rc = tcap_csl_tr_end_ind(tt, tcmsg, msg);
/* TSM-stopped to TCO */
tcap_tco_tsm_stopped_ind(tt);
tcap_trans_set_state(tt, TCAP_TS_INVALID);
return rc;
}
/* ABORT received (TCO -> TSM) */
int tcap_tsm_abort_rcvd(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
{
int rc = 0;
switch (tt->state) {
case TCAP_TS_INIT_SENT:
case TCAP_TS_ACTIVE:
break;
default:
return -EINVAL;
}
if (0 /* TR-U-ABORT */) {
/* TR-U-ABORT.ind -> CSL */
rc = tcap_csl_tr_u_abort_ind(tt, tcmsg, msg);
}
/* TSM-stopped to TCO */
tcap_tco_tsm_stopped_ind(tt);
tcap_trans_set_state(tt, TCAP_TS_INVALID);
return rc;
}
/* Local Abort (TCO -> TSM) */
int tcap_tsm_local_abort(struct tcap_transaction *tt)
{
int rc = 0;
switch (tt->state) {
case TCAP_TS_INIT_SENT:
case TCAP_TS_ACTIVE:
break;
default:
return -EINVAL;
}
/* TR-P-ABORT.ind -> CSL */
rc = tcap_csl_tr_p_abort_ind(tt);
/* TSM-stopped to TCO */
tcap_tco_tsm_stopped_ind(tt);
tcap_trans_set_state(tt, TCAP_TS_INVALID);
return rc;
}