From 32ff8b90f4384b5a8732df06c0a1f24da5c7ffb5 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 14 Apr 2018 10:57:41 +0200 Subject: msc: Add SMPP_Emulation + SMPP_Templates Change-Id: I80efe16f603227694c6242d556ae77590271e4c6 --- library/SMPP_Emulation.ttcn | 413 ++++++++++++++++++++++++++++++++++++++++++++ library/SMPP_Templates.ttcn | 125 ++++++++++++++ 2 files changed, 538 insertions(+) create mode 100644 library/SMPP_Emulation.ttcn create mode 100644 library/SMPP_Templates.ttcn (limited to 'library') diff --git a/library/SMPP_Emulation.ttcn b/library/SMPP_Emulation.ttcn new file mode 100644 index 00000000..83e7801f --- /dev/null +++ b/library/SMPP_Emulation.ttcn @@ -0,0 +1,413 @@ +module SMPP_Emulation { + +/* SMPP Emulation layer, sitting on top of SMPP_CodecPort. + * + * (C) 2018 by Harald Welte + * All rights reserved. + * + * Released under the terms of GNU General Public License, Version 2 or + * (at your option) any later version. + */ + + +import from Osmocom_Types all; +import from General_Types all; +import from SMPP_Types all; +import from SMPP_Templates all; +import from SMPP_CodecPort all; +import from SMPP_CodecPort_CtrlFunct all; +import from IPL4asp_Types all; +import from IPL4asp_PortType all; +import from Socket_API_Definitions all; + +/* general "base class" component definition, of which specific implementations + * derive themselves by menas of the "extends" feature */ +type component SMPP_ConnHdlr { + /* port towards SPPP_Emulation_CT */ + port SMPP_Conn_PT SMPP; + port SMPPEM_PROC_PT SMPP_PROC; +} + + +type component SMPP_Emulation_CT { + /* down-facing port to SMPP Codec port */ + port SMPP_CODEC_PT SMPP_PORT; + var IPL4asp_Types.ConnectionId g_smpp_conn_id := -1; + + var integer g_seq := 1; + + /* up-facing port to Clients */ + port SMPP_Conn_PT SMPP_CLIENT; + port SMPPEM_PROC_PT SMPP_PROC; + + var TransactionData TransactionTable[32]; + var ExpectData ExpectTable[32]; +} + +type port SMPP_Conn_PT message { + inout SMPP_PDU; +} with { extension "internal" }; + +type record TransactionData { + uint32_t tid optional, + SMPP_ConnHdlr vc_conn +} + +type record ExpectData { + SMPP_TON dst_ton optional, + SMPP_NPI dst_npi optional, + charstring dst_addr, + SMPP_ConnHdlr vc_conn +} + +private function f_trans_id_known(uint32_t tid) +runs on SMPP_Emulation_CT return boolean { + for (var integer i := 0; i < sizeof(TransactionTable); i := i+1) { + if (TransactionTable[i].tid == tid) { + return true; + } + } + return false; +} + +private function f_comp_known(SMPP_ConnHdlr client) +runs on SMPP_Emulation_CT return boolean { + for (var integer i := 0; i < sizeof(TransactionTable); i := i+1) { + if (TransactionTable[i].vc_conn == client) { + return true; + } + } + return false; +} + +private function f_comp_by_trans_id(uint32_t tid) +runs on SMPP_Emulation_CT return SMPP_ConnHdlr { + for (var integer i := 0; i < sizeof(TransactionTable); i := i+1) { + if (TransactionTable[i].tid == tid) { + return TransactionTable[i].vc_conn; + } + } + setverdict(fail, "No componten for SMPP TID ", tid); + self.stop; +} + + +private function f_trans_table_init() +runs on SMPP_Emulation_CT { + for (var integer i := 0; i < sizeof(TransactionTable); i := i+1) { + TransactionTable[i].vc_conn := null; + TransactionTable[i].tid := omit; + } +} + +private function f_trans_table_add(SMPP_ConnHdlr vc_conn, uint32_t trans_id) +runs on SMPP_Emulation_CT { + for (var integer i := 0; i < sizeof(TransactionTable); i := i+1) { + if (TransactionTable[i].vc_conn == null) { + TransactionTable[i].vc_conn := vc_conn; + TransactionTable[i].tid := trans_id; + return; + } + } + setverdict(fail, "SMPP Trans table full!"); + self.stop; +} + +private function f_trans_table_del(uint32_t trans_id) +runs on SMPP_Emulation_CT { + for (var integer i := 0; i < sizeof(TransactionTable); i := i+1) { + if (TransactionTable[i].tid == trans_id) { + TransactionTable[i].vc_conn := null; + TransactionTable[i].tid := omit; + return; + } + } + setverdict(fail, "SMPP Trans table attempt to delete non-existant ", trans_id); + self.stop; +} + + + +function f_connect(charstring remote_host, IPL4asp_Types.PortNumber remote_port, + charstring local_host, IPL4asp_Types.PortNumber local_port) +runs on SMPP_Emulation_CT { + var IPL4asp_Types.Result res; + res := SMPP_CodecPort_CtrlFunct.f_IPL4_connect(SMPP_PORT, remote_host, remote_port, + local_host, local_port, 0, { tcp :={} }); + g_smpp_conn_id := res.connId; +} + +/* Function to use to bind to a local port as IPA server, accepting remote clients */ +function f_bind(charstring local_host, IPL4asp_Types.PortNumber local_port) +runs on SMPP_Emulation_CT { + var IPL4asp_Types.Result res; + res := SMPP_CodecPort_CtrlFunct.f_IPL4_listen(SMPP_PORT, local_host, local_port, { tcp:={} }); + g_smpp_conn_id := res.connId; +} + + +function main_server(EsmePars pars, charstring local_host, integer local_port) +runs on SMPP_Emulation_CT { + f_bind(local_host, local_port); + f_mainloop(pars); +} + +function main_client(EsmePars pars, charstring remote_host, integer remote_port, + charstring local_host, integer local_port) +runs on SMPP_Emulation_CT { + f_connect(remote_host, remote_port, local_host, local_port); + f_mainloop(pars); +} + +type enumerated EsmeMode { + MODE_TRANSMITTER, + MODE_RECEIVER, + MODE_TRANSCEIVER +} +type record EsmePars { + EsmeMode mode, + SMPP_Bind bind, + boolean esme_role +} + +private function f_tx_smpp(template (value) SMPP_PDU pdu) runs on SMPP_Emulation_CT { + pdu.header.seq_num := g_seq; + SMPP_PORT.send(ts_SMPP_Send(g_smpp_conn_id, pdu)); + g_seq := g_seq+1; +} + +private function f_rx_smpp(template SMPP_PDU pdu) runs on SMPP_Emulation_CT { + timer T_wait := 3.0; + T_wait.start; + alt { + [] SMPP_PORT.receive(tr_SMPP_Recv(g_smpp_conn_id, pdu)) { } + [] T_wait.timeout { + setverdict(fail, "Timeout waiting for ", pdu); + self.stop; + } + } +} + +/* default altstep which we use throughout */ +private altstep as_smpp() runs on SMPP_Emulation_CT { + var SMPP_ConnHdlr vc_conn; + var SMPP_RecvFrom smpp_rf; + /* Answer to ENQUIRE LINK */ + [] SMPP_PORT.receive(tr_SMPP_Recv(g_smpp_conn_id, + tr_SMPP(c_SMPP_command_id_enquire_link, ESME_ROK))) { + f_tx_smpp(ts_SMPP_ENQ_LINK_resp); + } + [] SMPP_PORT.receive(tr_SMPP_Recv(g_smpp_conn_id, + tr_SMPP(c_SMPP_command_id_alert_notification, ESME_ROK))) -> value smpp_rf { + /* TODO: dispatch to ConnHdlr based on some kind of expect mechanism? */ + vc_conn := f_exp_lookup(smpp_rf.msg.body.alert_notif.source_addr_ton, + smpp_rf.msg.body.alert_notif.source_addr_npi, + smpp_rf.msg.body.alert_notif.source_addr); + SMPP_CLIENT.send(smpp_rf.msg) to vc_conn; + } + [] SMPP_PORT.receive { + setverdict(fail, "Unexpected SMPP from peer"); + self.stop; + } +} + +function f_mainloop(EsmePars pars) +runs on SMPP_Emulation_CT { + + /* Set function for dissecting the binary stream into packets */ + var f_IPL4_getMsgLen vl_f := refers(f_IPL4_fixedMsgLen); + /* Offset: 0, size of length: 4, delta: 0, multiplier: 1, big-endian */ + SMPP_CodecPort_CtrlFunct.f_IPL4_setGetMsgLen(SMPP_PORT, g_smpp_conn_id, vl_f, {0, 4, 0, 1, 0}); + + f_trans_table_init(); + f_expect_table_init(); + + /* activate default altstep */ + var default d := activate(as_smpp()); + + if (pars.esme_role) { + /* BIND to SMSC */ + select (pars.mode) { + case (MODE_TRANSMITTER) { + f_tx_smpp(ts_SMPP_BIND_TX(pars.bind)); + /* FIXME: do we have to check for SEQ? */ + f_rx_smpp(tr_SMPP(c_SMPP_command_id_bind_transmitter_resp, ESME_ROK, g_seq)); + } + case (MODE_RECEIVER) { + f_tx_smpp(ts_SMPP_BIND_RX(pars.bind)); + /* FIXME: do we have to check for SEQ? */ + f_rx_smpp(tr_SMPP(c_SMPP_command_id_bind_receiver_resp, ESME_ROK, g_seq)); + } + case (MODE_TRANSCEIVER) { + f_tx_smpp(ts_SMPP_BIND_TRX(pars.bind)); + /* FIXME: do we have to check for SEQ? */ + f_rx_smpp(tr_SMPP(c_SMPP_command_id_bind_transceiver_resp, ESME_ROK)); + } + } + } else { + var SMPP_Bind_resp bresp := { + system_id := pars.bind.system_id, + opt_pars := {} + } + /* Expect bind from ESME */ + select (pars.mode) { + case (MODE_TRANSMITTER) { + f_rx_smpp(tr_SMPP_BIND_TX(pars.bind)); + /* FIXME: do we have to check for SEQ? */ + f_tx_smpp(ts_SMPP_BIND_TX_resp(ESME_ROK, bresp)); + } + case (MODE_RECEIVER) { + f_rx_smpp(tr_SMPP_BIND_RX(pars.bind)); + /* FIXME: do we have to check for SEQ? */ + f_tx_smpp(ts_SMPP_BIND_RX_resp(ESME_ROK, bresp)); + } + case (MODE_TRANSCEIVER) { + f_rx_smpp(tr_SMPP_BIND_TRX(pars.bind)); + /* FIXME: do we have to check for SEQ? */ + f_tx_smpp(ts_SMPP_BIND_TRX_resp(ESME_ROK, bresp)); + } + } + } + + while (true) { + var SMPP_ConnHdlr vc_conn; + var SMPP_RecvFrom smpp_rf; + var SMPP_PDU smpp; + var charstring dest_addr; + alt { + /* SMSC -> CLIENT: response, map by seq_nr */ + [pars.esme_role] SMPP_PORT.receive(tr_SMPP_Recv(g_smpp_conn_id, + tr_SMPP_esme_resp)) -> value smpp_rf { + var uint32_t trans_id := smpp_rf.msg.header.seq_num; + if (f_trans_id_known(trans_id)) { + vc_conn := f_comp_by_trans_id(trans_id); + SMPP_CLIENT.send(smpp_rf.msg) to vc_conn; + f_trans_table_del(trans_id); + } else { + log("Received SMPP response for unknown trans_id ", smpp_rf); + /* FIXME */ + } + } + /* SMSC -> CLIENT: DELIVER-SM.req */ + [pars.esme_role] SMPP_PORT.receive(tr_SMPP_Recv(g_smpp_conn_id, + tr_SMPP(c_SMPP_command_id_deliver_sm, ESME_ROK))) -> value smpp_rf { + vc_conn := f_exp_lookup(smpp_rf.msg.body.deliver_sm.dest_addr_ton, + smpp_rf.msg.body.deliver_sm.dest_addr_npi, + smpp_rf.msg.body.deliver_sm.destination_addr); + SMPP_CLIENT.send(smpp_rf.msg) to vc_conn; + } + + /* record seq_nr for commands from CLIENT -> SMSC */ + [pars.esme_role] SMPP_CLIENT.receive(tr_SMPP_esme_req) -> value smpp sender vc_conn { + /* register current seq_nr/trans_id */ + f_trans_table_add(vc_conn, g_seq); + f_tx_smpp(smpp); + } + /* pass responses 1:1 through from CLIENT -> SMSC */ + [pars.esme_role] SMPP_CLIENT.receive(tr_SMPP_smsc_resp) -> value smpp sender vc_conn { + SMPP_PORT.send(ts_SMPP_Send(g_smpp_conn_id, smpp)); + } + + [] SMPP_PROC.getcall(SMPPEM_register:{?,?}) -> param(dest_addr, vc_conn) { + f_create_expect(dest_addr, vc_conn); + SMPP_PROC.reply(SMPPEM_register:{dest_addr, vc_conn}); + } + } + } +} + +/* Requests from ESME -> SMSC */ +template OCT4 SMPP_esme_req := ( + c_SMPP_command_id_submit_sm, + c_SMPP_command_id_replace_sm, + c_SMPP_command_id_cancel_sm, + c_SMPP_command_id_submit_multi +); +template SMPP_PDU tr_SMPP_esme_req := tr_SMPP(SMPP_esme_req, ?); + +/* Responses from ESME -> SMSC */ +template OCT4 SMPP_esme_resp := ( + c_SMPP_command_id_submit_sm_resp, + c_SMPP_command_id_replace_sm_resp, + c_SMPP_command_id_cancel_sm_resp, + c_SMPP_command_id_submit_multi_resp +); +template SMPP_PDU tr_SMPP_esme_resp := tr_SMPP(SMPP_esme_resp, ?); + +/* Requests from SMSC -> ESME */ +template OCT4 SMPP_smsc_req := ( + c_SMPP_command_id_deliver_sm +); +template SMPP_PDU tr_SMPP_smsc_req := tr_SMPP(SMPP_smsc_req, ?); + +/* Responses from SMSC -> ESME */ +template OCT4 SMPP_smsc_resp := ( + c_SMPP_command_id_deliver_sm_resp +); +template SMPP_PDU tr_SMPP_smsc_resp := tr_SMPP(SMPP_smsc_resp, ?); + + + +signature SMPPEM_register(charstring dst_addr, SMPP_ConnHdlr hdlr); + +type port SMPPEM_PROC_PT procedure { + inout SMPPEM_register; +} with { extension "internal" }; + +private function f_create_expect(charstring dest_number, SMPP_ConnHdlr hdlr) +runs on SMPP_Emulation_CT { + for (var integer i := 0; i < sizeof(ExpectTable); i := i+1) { + if (ExpectTable[i].vc_conn == null) { + ExpectTable[i] := { + dst_ton := omit, + dst_npi := omit, + dst_addr := dest_number, + vc_conn := hdlr + } + return; + } + } + setverdict(fail, "No space left in SmppExpectTable"); + self.stop; +} + +private function f_exp_lookup(SMPP_TON ton, SMPP_NPI npi, charstring dst) +runs on SMPP_Emulation_CT return SMPP_ConnHdlr { + for (var integer i := 0; i < sizeof(ExpectTable); i := i+1) { + if (ExpectTable[i].vc_conn != null and ExpectTable[i].dst_addr == dst) { + if (ispresent(ExpectTable[i].dst_ton) and ExpectTable[i].dst_ton != ton) { + continue; + } + if (ispresent(ExpectTable[i].dst_npi) and ExpectTable[i].dst_npi != npi) { + continue; + } + return ExpectTable[i].vc_conn; + } + } + return null; +} +private function f_expect_table_init() +runs on SMPP_Emulation_CT { + for (var integer i := 0; i < sizeof(ExpectTable); i := i+1) { + ExpectTable[i] := { + dst_ton := omit, + dst_npi := omit, + dst_addr := "", + vc_conn := null + }; + } +} + + + + + +/* client/conn_hdlr side function to use procedure port to create expect in emulation */ +function f_create_smpp_expect(charstring dest_number) runs on SMPP_ConnHdlr { + SMPP_PROC.call(SMPPEM_register:{dest_number, self}) { + [] SMPP_PROC.getreply(SMPPEM_register:{?,?}) {}; + } +} + + +} diff --git a/library/SMPP_Templates.ttcn b/library/SMPP_Templates.ttcn new file mode 100644 index 00000000..9a56cf2c --- /dev/null +++ b/library/SMPP_Templates.ttcn @@ -0,0 +1,125 @@ +module SMPP_Templates { + +import from General_Types all; +import from SMPP_Types all; + +template (value) SMPP_header ts_SMPP_hdr(OCT4 command_id, SMPP_error_code status, + integer seq := 0) := { + command_len := 0, + command_id := command_id, + command_status := status, + seq_num := seq +} +template SMPP_header tr_SMPP_hdr(template OCT4 command_id, template SMPP_error_code status, + template integer seq := ?) := { + command_len := ?, + command_id := command_id, + command_status := status, + seq_num := seq +} + +template (value) SMPP_PDU ts_SMPP(OCT4 command_id, SMPP_error_code status, + template (value) SMPP_operation_body body) := { + header := ts_SMPP_hdr(command_id, status), + body := body +} +template SMPP_PDU tr_SMPP(template OCT4 command_id, template SMPP_error_code status, + template integer seq := ?, + template SMPP_operation_body body := ?) := { + header := tr_SMPP_hdr(command_id, status, seq), + body := body +} + + + +template (value) SMPP_PDU ts_SMPP_BIND_TX(template (value) SMPP_Bind bind) := { + header := ts_SMPP_hdr(c_SMPP_command_id_bind_transmitter, ESME_ROK), + body := { + bind_transmitter := bind + } +} +template SMPP_PDU tr_SMPP_BIND_TX(template (value) SMPP_Bind bind, template integer seq := ?) := { + header := tr_SMPP_hdr(c_SMPP_command_id_bind_transmitter, ESME_ROK, seq), + body := { + bind_transmitter := bind + } +} + +template (value) SMPP_PDU ts_SMPP_BIND_TX_resp(SMPP_error_code status, + template (value) SMPP_Bind_resp bind) := { + header := ts_SMPP_hdr(c_SMPP_command_id_bind_transmitter_resp, status), + body := { + bind_transmitter_resp := bind + } +} + +template (value) SMPP_PDU ts_SMPP_BIND_RX(template (value) SMPP_Bind bind) := { + header := ts_SMPP_hdr(c_SMPP_command_id_bind_receiver, ESME_ROK), + body := { + bind_receiver := bind + } +} +template SMPP_PDU tr_SMPP_BIND_RX(template (value) SMPP_Bind bind, template integer seq := ?) := { + header := tr_SMPP_hdr(c_SMPP_command_id_bind_receiver, ESME_ROK, seq), + body := { + bind_receiver := bind + } +} + +template (value) SMPP_PDU ts_SMPP_BIND_RX_resp(SMPP_error_code status, + template (value) SMPP_Bind_resp bind) := { + header := ts_SMPP_hdr(c_SMPP_command_id_bind_receiver_resp, status), + body := { + bind_receiver_resp := bind + } +} + +template (value) SMPP_PDU ts_SMPP_BIND_TRX(template (value) SMPP_Bind bind) := { + header := ts_SMPP_hdr(c_SMPP_command_id_bind_transceiver, ESME_ROK), + body := { + bind_transceiver := bind + } +} +template SMPP_PDU tr_SMPP_BIND_TRX(template (value) SMPP_Bind bind, template integer seq := ?) := { + header := tr_SMPP_hdr(c_SMPP_command_id_bind_transceiver, ESME_ROK, seq), + body := { + bind_transceiver := bind + } +} + +template (value) SMPP_PDU ts_SMPP_BIND_TRX_resp(SMPP_error_code status, + template (value) SMPP_Bind_resp bind) := { + header := ts_SMPP_hdr(c_SMPP_command_id_bind_transceiver_resp, status), + body := { + bind_transceiver_resp := bind + } +} + +template (value) SMPP_PDU ts_SMPP_ENQ_LINK := { + header := ts_SMPP_hdr(c_SMPP_command_id_enquire_link, ESME_ROK), + body := { + enquire_link := {} + } +} + +template (value) SMPP_PDU ts_SMPP_ENQ_LINK_resp := { + header := ts_SMPP_hdr(c_SMPP_command_id_enquire_link_resp, ESME_ROK), + body := { + enquire_link_resp := {} + } +} + +template (value) SMPP_PDU ts_SMPP_DELIVER_SM_resp(SMPP_error_code status, integer seq) := { + header := ts_SMPP_hdr(c_SMPP_command_id_deliver_sm_resp, status, seq), + body := { + deliver_sm_resp := { + message_id := "", /* unused */ + opt_pars := omit + } + } +} + + + + +} -- cgit v1.2.3