297 lines
6.7 KiB
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;
|
|
}
|