/* ITU-T Q.77x TCAP / TSM - Transaction State Machine */ /* (C) 2010 by Harald Welte * (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 . */ #include #include #include #include #include #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; }