| module MGCP_Emulation { |
| |
| /* MGCP Emulation, runs on top of MGCP_CodecPort. It multiplexes/demultiplexes |
| * the individual connections, so there can be separate TTCN-3 components handling |
| * each of the connections. |
| * |
| * The MGCP_Emulation.main() function processes MGCP primitives from the MGCP |
| * socket via the MGCP_CodecPort, and dispatches them to the per-connection components. |
| * |
| * For each new inbound connection, the MgcpOps.create_cb() is called. It can create |
| * or resolve a TTCN-3 component, and returns a component reference to which that inbound |
| * connection is routed/dispatched. |
| * |
| * If a pre-existing component wants to register to handle a future inbound call, it can |
| * do so by registering an "expect" with the expected destination phone number. This is e.g. useful |
| * if you are simulating BSC + MGCP, and first trigger a connection from BSC side in a |
| * component which then subsequently should also handle the MGCP emulation. |
| * |
| * Inbound Unit Data messages (such as are dispatched to the MgcpOps.unitdata_cb() callback, |
| * which is registered with an argument to the main() function below. |
| * |
| * (C) 2017-2018 by Harald Welte <laforge@gnumonks.org> |
| * (C) 2018 by sysmocom - s.f.m.c. GmbH, Author: Daniel Willmann |
| * All rights reserved. |
| * |
| * Released under the terms of GNU General Public License, Version 2 or |
| * (at your option) any later version. |
| */ |
| |
| import from MGCP_CodecPort all; |
| import from MGCP_CodecPort_CtrlFunct all; |
| import from MGCP_Types all; |
| import from MGCP_Templates all; |
| import from Osmocom_Types all; |
| import from IPL4asp_Types all; |
| |
| type component MGCP_ConnHdlr { |
| port MGCP_Conn_PT MGCP; |
| var MgcpConnectionId mgcp_conn_id; |
| } |
| |
| /* port between individual per-connection components and this dispatcher */ |
| type port MGCP_Conn_PT message { |
| inout MgcpCommand, MgcpResponse; |
| } with { extension "internal" }; |
| |
| |
| type component MGCP_Emulation_CT { |
| /* Port facing to the UDP SUT */ |
| port MGCP_CODEC_PT MGCP; |
| /* All MGCP_ConnHdlr MGCP ports connect here |
| * MGCP_Emulation_CT.main needs to figure out what messages |
| * to send where with CLIENT.send() to vc_conn */ |
| port MGCP_Conn_PT CLIENT; |
| /* currently tracked connections */ |
| // var ConnectionData ConnectionTable[16]; |
| /* pending expected CRCX */ |
| var ExpectData ExpectTable[8]; |
| /* procedure based port to register for incoming connections */ |
| port MGCPEM_PROC_PT PROC; |
| |
| var charstring g_mgcp_id; |
| var integer g_mgcp_conn_id := -1; |
| } |
| |
| type function MGCPCreateCallback(MgcpCommand cmd, charstring id) |
| runs on MGCP_Emulation_CT return MGCP_ConnHdlr; |
| |
| type record MGCPOps { |
| MGCPCreateCallback create_cb |
| } |
| |
| type record MGCP_conn_parameters { |
| charstring callagent_ip, |
| uint16_t callagent_udp_port, |
| charstring mgw_ip, |
| uint16_t mgw_udp_port |
| } |
| |
| function tr_MGCP_RecvFrom_R(template MgcpMessage msg) |
| runs on MGCP_Emulation_CT return template MGCP_RecvFrom { |
| var template MGCP_RecvFrom mrf := { |
| connId := g_mgcp_conn_id, |
| remName := ?, |
| remPort := ?, |
| locName := ?, |
| locPort := ?, |
| msg := msg |
| } |
| return mrf; |
| } |
| |
| function main(MGCPOps ops, MGCP_conn_parameters p, charstring id) runs on MGCP_Emulation_CT { |
| var Result res; |
| g_mgcp_id := id; |
| //f_conn_table_init(); |
| f_expect_table_init(); |
| |
| map(self:MGCP, system:MGCP_CODEC_PT); |
| res := MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, p.mgw_ip, |
| p.mgw_udp_port, |
| p.callagent_ip, p.callagent_udp_port, 0, { udp:={} }); |
| |
| g_mgcp_conn_id := res.connId; |
| |
| while (true) { |
| var MGCP_ConnHdlr vc_conn; |
| var ExpectCriteria crit; |
| var MGCP_RecvFrom mrf; |
| var MgcpMessage msg; |
| var MgcpCommand cmd; |
| var MgcpResponse resp; |
| |
| alt { |
| /* MGCP from client */ |
| [] CLIENT.receive(MgcpResponse:?) -> value resp sender vc_conn { |
| /* Pass message through */ |
| msg.response := resp; |
| MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg)); |
| } |
| [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) -> value mrf { |
| if (ischosen(mrf.msg.command)) { |
| cmd := mrf.msg.command; |
| vc_conn := ops.create_cb.apply(cmd, id); |
| f_handle_userData(vc_conn, cmd); |
| } else { |
| setverdict(fail, "Received unexpected MGCP response: ", mrf.msg.response); |
| self.stop; |
| } |
| } |
| [] PROC.getcall(MGCPEM_register:{?,?}) -> param(crit, vc_conn) { |
| f_create_expect(crit, vc_conn); |
| PROC.reply(MGCPEM_register:{crit, vc_conn}); |
| } |
| } |
| } |
| } |
| |
| private function f_handle_userData(MGCP_ConnHdlr conn, MgcpCommand cmd) |
| runs on MGCP_Emulation_CT { |
| CLIENT.send(cmd) to conn; |
| } |
| |
| /* "Expect" Handling */ |
| |
| /* */ |
| type record ExpectCriteria { |
| MgcpConnectionId connid optional, |
| MgcpEndpoint endpoint optional, |
| MgcpTransId transid optional |
| } |
| |
| type record ExpectData { |
| ExpectCriteria crit optional, |
| MGCP_ConnHdlr vc_conn |
| } |
| |
| signature MGCPEM_register(in ExpectCriteria cmd, in MGCP_ConnHdlr hdlr); |
| |
| type port MGCPEM_PROC_PT procedure { |
| inout MGCPEM_register; |
| } with { extension "internal" }; |
| |
| function f_get_mgcp_by_crit(ExpectCriteria crit) |
| return template MgcpCommand { |
| template MgcpCommand ret := { |
| }; |
| |
| return ret; |
| } |
| |
| /* Function that can be used as create_cb and will usse the expect table */ |
| function ExpectedCreateCallback(MgcpCommand cmd, charstring id) |
| runs on MGCP_Emulation_CT return MGCP_ConnHdlr { |
| var MGCP_ConnHdlr ret := null; |
| var template MgcpCommand mgcpcmd; |
| var integer i; |
| |
| /* Ensure cmd is a CRCX? */ |
| |
| for (i := 0; i < sizeof(ExpectTable); i := i+1) { |
| if (not ispresent(ExpectTable[i].crit)) { |
| continue; |
| } |
| /* FIXME: Ignore criteria for now */ |
| // mgcpcmd := f_get_mgcp_by_crit(ExpectTable[i].crit); |
| // if (match(cmd, mgcpcmd)) { |
| ret := ExpectTable[i].vc_conn; |
| /* Release this entry */ |
| ExpectTable[i].crit := omit; |
| ExpectTable[i].vc_conn := null; |
| log("Found Expect[", i, "] for ", cmd, " handled at ", ret); |
| return ret; |
| // } |
| } |
| setverdict(fail, "Couldn't find Expect for CRCX", cmd); |
| return ret; |
| } |
| |
| private function f_create_expect(ExpectCriteria crit, MGCP_ConnHdlr hdlr) |
| runs on MGCP_Emulation_CT { |
| var integer i; |
| |
| /* Check an entry like this is not already presnt */ |
| for (i := 0; i < sizeof(ExpectTable); i := i+1) { |
| if (crit == ExpectTable[i].crit) { |
| setverdict(fail, "Crit already present", crit); |
| self.stop; |
| } |
| } |
| for (i := 0; i < sizeof(ExpectTable); i := i+1) { |
| if (not ispresent(ExpectTable[i].crit)) { |
| ExpectTable[i].crit := crit; |
| ExpectTable[i].vc_conn := hdlr; |
| log("Created Expect[", i, "] for ", crit, " to be handled at ", hdlr); |
| return; |
| } |
| } |
| setverdict(fail, "No space left in ExpectTable") |
| } |
| |
| private function f_expect_table_init() |
| runs on MGCP_Emulation_CT { |
| var integer i; |
| for (i := 0; i < sizeof(ExpectTable); i := i + 1) { |
| ExpectTable[i].crit := omit; |
| } |
| } |
| |
| } |