blob: 074385cca9401e0bb0d33c613a7ece7328e13a31 [file] [log] [blame]
Daniel Willmannfa870f52018-01-17 12:37:14 +01001module MGCP_Emulation {
2
Harald Weltea02515f2018-01-26 10:33:23 +01003/* MGCP Emulation, runs on top of MGCP_CodecPort. It multiplexes/demultiplexes
4 * the individual connections, so there can be separate TTCN-3 components handling
5 * each of the connections.
6 *
7 * The MGCP_Emulation.main() function processes MGCP primitives from the MGCP
8 * socket via the MGCP_CodecPort, and dispatches them to the per-connection components.
9 *
10 * For each new inbound connection, the MgcpOps.create_cb() is called. It can create
11 * or resolve a TTCN-3 component, and returns a component reference to which that inbound
12 * connection is routed/dispatched.
13 *
14 * If a pre-existing component wants to register to handle a future inbound call, it can
15 * do so by registering an "expect" with the expected destination phone number. This is e.g. useful
16 * if you are simulating BSC + MGCP, and first trigger a connection from BSC side in a
17 * component which then subsequently should also handle the MGCP emulation.
18 *
19 * Inbound Unit Data messages (such as are dispatched to the MgcpOps.unitdata_cb() callback,
20 * which is registered with an argument to the main() function below.
21 *
22 * (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
23 * (C) 2018 by sysmocom - s.f.m.c. GmbH, Author: Daniel Willmann
24 * All rights reserved.
25 *
26 * Released under the terms of GNU General Public License, Version 2 or
27 * (at your option) any later version.
28 */
29
Daniel Willmannfa870f52018-01-17 12:37:14 +010030import from MGCP_CodecPort all;
31import from MGCP_CodecPort_CtrlFunct all;
32import from MGCP_Types all;
33import from MGCP_Templates all;
34import from Osmocom_Types all;
35import from IPL4asp_Types all;
36
37type component MGCP_ConnHdlr {
38 port MGCP_Conn_PT MGCP;
Harald Weltebb5a1212018-01-26 10:34:44 +010039 /* procedure based port to register for incoming connections */
40 port MGCPEM_PROC_PT MGCP_PROC;
Daniel Willmannaeea76f2018-01-17 15:23:45 +010041 var MgcpConnectionId mgcp_conn_id;
Daniel Willmannfa870f52018-01-17 12:37:14 +010042}
43
44/* port between individual per-connection components and this dispatcher */
45type port MGCP_Conn_PT message {
46 inout MgcpCommand, MgcpResponse;
47} with { extension "internal" };
48
Harald Welte9601c812018-01-26 18:58:20 +010049/* represents a single MGCP Endpoint */
50type record EndpointData {
Harald Weltebb5a1212018-01-26 10:34:44 +010051 MGCP_ConnHdlr comp_ref,
Harald Welte9601c812018-01-26 18:58:20 +010052 MgcpEndpoint endpoint optional
Harald Weltebb5a1212018-01-26 10:34:44 +010053};
Daniel Willmannfa870f52018-01-17 12:37:14 +010054
Harald Welte9601c812018-01-26 18:58:20 +010055/* pending CRCX with their transaction ID */
56type set of MgcpTransId MgcpTransIds;
57
Daniel Willmannfa870f52018-01-17 12:37:14 +010058type component MGCP_Emulation_CT {
59 /* Port facing to the UDP SUT */
60 port MGCP_CODEC_PT MGCP;
61 /* All MGCP_ConnHdlr MGCP ports connect here
62 * MGCP_Emulation_CT.main needs to figure out what messages
63 * to send where with CLIENT.send() to vc_conn */
Harald Weltebb5a1212018-01-26 10:34:44 +010064 port MGCP_Conn_PT MGCP_CLIENT;
Daniel Willmannfa870f52018-01-17 12:37:14 +010065 /* currently tracked connections */
Harald Welte9601c812018-01-26 18:58:20 +010066 var EndpointData MgcpEndpointTable[16];
67 var MgcpTransIds MgcpPendingTrans := {};
Daniel Willmannfa870f52018-01-17 12:37:14 +010068 /* pending expected CRCX */
Harald Weltebb5a1212018-01-26 10:34:44 +010069 var ExpectData MgcpExpectTable[8];
Daniel Willmannfa870f52018-01-17 12:37:14 +010070 /* procedure based port to register for incoming connections */
Harald Weltebb5a1212018-01-26 10:34:44 +010071 port MGCPEM_PROC_PT MGCP_PROC;
Daniel Willmannfa870f52018-01-17 12:37:14 +010072
73 var charstring g_mgcp_id;
Daniel Willmann166bbb32018-01-17 15:28:04 +010074 var integer g_mgcp_conn_id := -1;
Daniel Willmannfa870f52018-01-17 12:37:14 +010075}
76
77type function MGCPCreateCallback(MgcpCommand cmd, charstring id)
78runs on MGCP_Emulation_CT return MGCP_ConnHdlr;
79
Harald Weltebb5a1212018-01-26 10:34:44 +010080type function MGCPUnitdataCallback(MgcpMessage msg)
81runs on MGCP_Emulation_CT return template MgcpMessage;
82
Daniel Willmannfa870f52018-01-17 12:37:14 +010083type record MGCPOps {
Harald Weltebb5a1212018-01-26 10:34:44 +010084 MGCPCreateCallback create_cb,
85 MGCPUnitdataCallback unitdata_cb
Daniel Willmannfa870f52018-01-17 12:37:14 +010086}
87
88type record MGCP_conn_parameters {
Harald Weltebb5a1212018-01-26 10:34:44 +010089 HostName callagent_ip,
90 PortNumber callagent_udp_port,
91 HostName mgw_ip,
92 PortNumber mgw_udp_port
Daniel Willmannfa870f52018-01-17 12:37:14 +010093}
94
Daniel Willmann166bbb32018-01-17 15:28:04 +010095function tr_MGCP_RecvFrom_R(template MgcpMessage msg)
96runs on MGCP_Emulation_CT return template MGCP_RecvFrom {
97 var template MGCP_RecvFrom mrf := {
98 connId := g_mgcp_conn_id,
99 remName := ?,
100 remPort := ?,
101 locName := ?,
102 locPort := ?,
103 msg := msg
104 }
105 return mrf;
106}
107
Harald Welte9601c812018-01-26 18:58:20 +0100108private function f_ep_known(MgcpEndpoint ep)
Harald Weltebb5a1212018-01-26 10:34:44 +0100109runs on MGCP_Emulation_CT return boolean {
110 var integer i;
Harald Welte9601c812018-01-26 18:58:20 +0100111 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
112 if (MgcpEndpointTable[i].endpoint == ep) {
Harald Weltebb5a1212018-01-26 10:34:44 +0100113 return true;
114 }
115 }
116 return false;
117}
118
119private function f_comp_known(MGCP_ConnHdlr client)
120runs on MGCP_Emulation_CT return boolean {
121 var integer i;
Harald Welte9601c812018-01-26 18:58:20 +0100122 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
123 if (MgcpEndpointTable[i].comp_ref == client) {
Harald Weltebb5a1212018-01-26 10:34:44 +0100124 return true;
125 }
126 }
127 return false;
128}
129
Harald Welte9601c812018-01-26 18:58:20 +0100130private function f_comp_by_ep(MgcpEndpoint ep)
Harald Weltebb5a1212018-01-26 10:34:44 +0100131runs on MGCP_Emulation_CT return MGCP_ConnHdlr {
132 var integer i;
Harald Welte9601c812018-01-26 18:58:20 +0100133 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
134 if (MgcpEndpointTable[i].endpoint == ep) {
135 return MgcpEndpointTable[i].comp_ref;
Harald Weltebb5a1212018-01-26 10:34:44 +0100136 }
137 }
Harald Welte9601c812018-01-26 18:58:20 +0100138 log("MGCP Endpoint Table not found by Endpoint", ep);
Harald Weltebb5a1212018-01-26 10:34:44 +0100139 setverdict(fail);
140 self.stop;
141}
142
Harald Welte9601c812018-01-26 18:58:20 +0100143private function f_ep_by_comp(MGCP_ConnHdlr client)
144runs on MGCP_Emulation_CT return MgcpEndpoint {
Harald Weltebb5a1212018-01-26 10:34:44 +0100145 var integer i;
Harald Welte9601c812018-01-26 18:58:20 +0100146 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
147 if (MgcpEndpointTable[i].comp_ref == client) {
148 return MgcpEndpointTable[i].endpoint;
Harald Weltebb5a1212018-01-26 10:34:44 +0100149 }
150 }
Harald Welte9601c812018-01-26 18:58:20 +0100151 log("MGCP Endpoint Table not found by component ", client);
Harald Weltebb5a1212018-01-26 10:34:44 +0100152 setverdict(fail);
153 self.stop;
154}
155
Harald Welte9601c812018-01-26 18:58:20 +0100156private function f_ep_table_add(MGCP_ConnHdlr comp_ref, MgcpEndpoint ep)
157runs on MGCP_Emulation_CT {
158 var integer i;
159 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
160 if (not isvalue(MgcpEndpointTable[i].endpoint)) {
161 MgcpEndpointTable[i].endpoint := ep;
162 MgcpEndpointTable[i].comp_ref := comp_ref;
163 return;
164 }
165 }
166 setverdict(fail, "MGCP Endpoint Table full!");
167 self.stop;
168}
169
170private function f_ep_table_del(MGCP_ConnHdlr comp_ref, MgcpEndpoint ep)
171runs on MGCP_Emulation_CT {
172 var integer i;
173 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
174 if (MgcpEndpointTable[i].comp_ref == comp_ref and
175 MgcpEndpointTable[i].endpoint == ep) {
176 MgcpEndpointTable[i].endpoint := omit;
177 MgcpEndpointTable[i].comp_ref := null;
178 return;
179 }
180 }
181 setverdict(fail, "MGCP Endpoint Table: Couldn't find to-be-deleted entry!");
182 self.stop;
183}
184
185
186/* Check if the given transaction ID is a pending CRCX. If yes, return true + remove */
187private function f_trans_id_was_pending(MgcpTransId trans_id)
188runs on MGCP_Emulation_CT return boolean {
189 for (var integer i := 0; i < lengthof(MgcpPendingTrans); i := i+1) {
190 if (MgcpPendingTrans[i] == trans_id) {
191 /* Remove from list */
192 var MgcpTransIds OldPendingTrans := MgcpPendingTrans;
193 MgcpPendingTrans := {}
194 for (var integer j := 0; j < lengthof(OldPendingTrans); j := j+1) {
195 if (j != i) {
196 MgcpPendingTrans := MgcpPendingTrans & {OldPendingTrans[j]};
197 }
198 }
199 return true;
200 }
201 }
202 return false;
203}
204
Harald Weltebb5a1212018-01-26 10:34:44 +0100205/* TODO: move this to MGCP_Types? */
Harald Welte9601c812018-01-26 18:58:20 +0100206function f_mgcp_ep(MgcpMessage msg) return MgcpEndpoint {
Harald Weltebb5a1212018-01-26 10:34:44 +0100207 var MgcpParameterList params;
208 var integer i;
209 if (ischosen(msg.command)) {
Harald Welte9601c812018-01-26 18:58:20 +0100210 return msg.command.line.ep;
Harald Weltebb5a1212018-01-26 10:34:44 +0100211 } else {
Harald Welte9601c812018-01-26 18:58:20 +0100212 /* FIXME */
213 return "null@none";
Harald Weltebb5a1212018-01-26 10:34:44 +0100214 }
Harald Weltebb5a1212018-01-26 10:34:44 +0100215}
216
Harald Welte9601c812018-01-26 18:58:20 +0100217private function f_ep_table_init()
Harald Weltebb5a1212018-01-26 10:34:44 +0100218runs on MGCP_Emulation_CT {
Harald Welte9601c812018-01-26 18:58:20 +0100219 for (var integer i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
220 MgcpEndpointTable[i].comp_ref := null;
221 MgcpEndpointTable[i].endpoint := omit;
Harald Weltebb5a1212018-01-26 10:34:44 +0100222 }
223}
224
Daniel Willmann955627a2018-01-17 15:22:32 +0100225function main(MGCPOps ops, MGCP_conn_parameters p, charstring id) runs on MGCP_Emulation_CT {
Daniel Willmannfa870f52018-01-17 12:37:14 +0100226 var Result res;
227 g_mgcp_id := id;
Harald Welte9601c812018-01-26 18:58:20 +0100228 f_ep_table_init();
Daniel Willmann955627a2018-01-17 15:22:32 +0100229 f_expect_table_init();
Daniel Willmannfa870f52018-01-17 12:37:14 +0100230
231 map(self:MGCP, system:MGCP_CODEC_PT);
Harald Weltebb5a1212018-01-26 10:34:44 +0100232 if (p.callagent_udp_port == -1) {
233 res := MGCP_CodecPort_CtrlFunct.f_IPL4_listen(MGCP, p.mgw_ip, p.mgw_udp_port, { udp:={} });
234 } else {
235 res := MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, p.callagent_ip, p.callagent_udp_port, p.mgw_ip, p.mgw_udp_port, -1, { udp:={} });
236 }
237
Daniel Willmann166bbb32018-01-17 15:28:04 +0100238 g_mgcp_conn_id := res.connId;
Harald Weltebb5a1212018-01-26 10:34:44 +0100239
Daniel Willmannfa870f52018-01-17 12:37:14 +0100240 while (true) {
Daniel Willmann166bbb32018-01-17 15:28:04 +0100241 var MGCP_ConnHdlr vc_conn;
242 var ExpectCriteria crit;
243 var MGCP_RecvFrom mrf;
244 var MgcpMessage msg;
245 var MgcpCommand cmd;
246 var MgcpResponse resp;
Harald Welte9601c812018-01-26 18:58:20 +0100247 var MgcpEndpoint ep;
Daniel Willmann166bbb32018-01-17 15:28:04 +0100248
Daniel Willmannfa870f52018-01-17 12:37:14 +0100249 alt {
Daniel Willmann166bbb32018-01-17 15:28:04 +0100250 /* MGCP from client */
Harald Weltebb5a1212018-01-26 10:34:44 +0100251 [] MGCP_CLIENT.receive(MgcpResponse:?) -> value resp sender vc_conn {
Harald Welte9601c812018-01-26 18:58:20 +0100252 msg := {
253 response := resp
254 };
255 /* If this is the resposne to a pending CRCX, extract Endpoint and store in table */
256 if (f_trans_id_was_pending(resp.line.trans_id)) {
257 f_ep_table_add(vc_conn, f_mgcp_ep(msg));
258 }
Daniel Willmann166bbb32018-01-17 15:28:04 +0100259 /* Pass message through */
Harald Weltebb5a1212018-01-26 10:34:44 +0100260 /* TODO: check which ConnectionID client has allocated + store in table? */
Daniel Willmann166bbb32018-01-17 15:28:04 +0100261 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Daniel Willmannfa870f52018-01-17 12:37:14 +0100262 }
Daniel Willmann166bbb32018-01-17 15:28:04 +0100263 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) -> value mrf {
Harald Weltebb5a1212018-01-26 10:34:44 +0100264 if (p.callagent_udp_port == -1) {
265 /* we aren't yet connected to the remote side port, let's fix this */
266 p.callagent_udp_port := mrf.remPort;
267 MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, p.callagent_ip, p.callagent_udp_port, p.mgw_ip, p.mgw_udp_port, g_mgcp_conn_id, { udp:={} });
268 }
Daniel Willmann166bbb32018-01-17 15:28:04 +0100269 if (ischosen(mrf.msg.command)) {
270 cmd := mrf.msg.command;
Harald Welte9601c812018-01-26 18:58:20 +0100271 if (f_ep_known(cmd.line.ep)) {
272 vc_conn := f_comp_by_ep(cmd.line.ep);
Harald Weltebb5a1212018-01-26 10:34:44 +0100273 MGCP_CLIENT.send(cmd) to vc_conn;
274 } else {
Harald Welte9601c812018-01-26 18:58:20 +0100275 if (cmd.line.verb == "CRCX") {
276 vc_conn := ops.create_cb.apply(cmd, id);
277 if (true /* non-wildcard EP */) {
278 f_ep_table_add(vc_conn, cmd.line.ep);
279 } else {
280 /* add this transaction to list of pending transactions */
281 MgcpPendingTrans := MgcpPendingTrans & {cmd.line.trans_id};
282 }
283 MGCP_CLIENT.send(cmd) to vc_conn;
284 } else {
285 /* connectionless MGCP, i.e. messages without ConnectionId */
286 var template MgcpMessage r := ops.unitdata_cb.apply(mrf.msg);
287 if (isvalue(r)) {
288 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, r));
289 }
Harald Weltebb5a1212018-01-26 10:34:44 +0100290 }
291 }
Daniel Willmann166bbb32018-01-17 15:28:04 +0100292 } else {
293 setverdict(fail, "Received unexpected MGCP response: ", mrf.msg.response);
294 self.stop;
295 }
Daniel Willmannfa870f52018-01-17 12:37:14 +0100296 }
Harald Weltebb5a1212018-01-26 10:34:44 +0100297 [] MGCP_PROC.getcall(MGCPEM_register:{?,?}) -> param(crit, vc_conn) {
Daniel Willmann955627a2018-01-17 15:22:32 +0100298 f_create_expect(crit, vc_conn);
Harald Weltebb5a1212018-01-26 10:34:44 +0100299 MGCP_PROC.reply(MGCPEM_register:{crit, vc_conn});
Daniel Willmann955627a2018-01-17 15:22:32 +0100300 }
Harald Welte9601c812018-01-26 18:58:20 +0100301 [] MGCP_PROC.getcall(MGCPEM_delete_ep:{?,?}) -> param(ep, vc_conn) {
302 f_ep_table_del(vc_conn, ep);
303 MGCP_PROC.reply(MGCPEM_delete_ep:{ep, vc_conn});
304 }
Daniel Willmannfa870f52018-01-17 12:37:14 +0100305 }
Harald Welte9601c812018-01-26 18:58:20 +0100306
Daniel Willmannfa870f52018-01-17 12:37:14 +0100307 }
308}
309
310/* "Expect" Handling */
311
312/* */
Daniel Willmann955627a2018-01-17 15:22:32 +0100313type record ExpectCriteria {
314 MgcpConnectionId connid optional,
315 MgcpEndpoint endpoint optional,
316 MgcpTransId transid optional
Daniel Willmannfa870f52018-01-17 12:37:14 +0100317}
318
319type record ExpectData {
320 ExpectCriteria crit optional,
Daniel Willmann955627a2018-01-17 15:22:32 +0100321 MGCP_ConnHdlr vc_conn
Daniel Willmannfa870f52018-01-17 12:37:14 +0100322}
323
Daniel Willmann955627a2018-01-17 15:22:32 +0100324signature MGCPEM_register(in ExpectCriteria cmd, in MGCP_ConnHdlr hdlr);
Harald Welte9601c812018-01-26 18:58:20 +0100325signature MGCPEM_delete_ep(in MgcpEndpoint ep, in MGCP_ConnHdlr hdlr);
Daniel Willmannfa870f52018-01-17 12:37:14 +0100326
327type port MGCPEM_PROC_PT procedure {
Harald Welte9601c812018-01-26 18:58:20 +0100328 inout MGCPEM_register, MGCPEM_delete_ep;
Daniel Willmannfa870f52018-01-17 12:37:14 +0100329} with { extension "internal" };
330
331function f_get_mgcp_by_crit(ExpectCriteria crit)
332return template MgcpCommand {
Harald Weltebb5a1212018-01-26 10:34:44 +0100333 var template MgcpCommand ret := {
334 line := {
335 verb := ?,
336 trans_id := ?,
337 ep := ?,
338 ver := ?
339 },
340 params := *,
341 sdp := *
342 }
343 if (ispresent(crit.connid)) {
344 ret.params := { *, ts_MgcpParConnectionId(crit.connid), * };
345 }
346 if (ispresent(crit.endpoint)) {
347 ret.line.ep := crit.endpoint;
348 }
349 if (ispresent(crit.transid)) {
350 ret.line.trans_id := crit.transid;
351 }
Daniel Willmannfa870f52018-01-17 12:37:14 +0100352
353 return ret;
354}
355
356/* Function that can be used as create_cb and will usse the expect table */
357function ExpectedCreateCallback(MgcpCommand cmd, charstring id)
358runs on MGCP_Emulation_CT return MGCP_ConnHdlr {
359 var MGCP_ConnHdlr ret := null;
360 var template MgcpCommand mgcpcmd;
361 var integer i;
362
Harald Weltebb5a1212018-01-26 10:34:44 +0100363 for (i := 0; i < sizeof(MgcpExpectTable); i := i+1) {
364 if (not ispresent(MgcpExpectTable[i].crit)) {
Daniel Willmannfa870f52018-01-17 12:37:14 +0100365 continue;
366 }
Daniel Willmann955627a2018-01-17 15:22:32 +0100367 /* FIXME: Ignore criteria for now */
Harald Weltebb5a1212018-01-26 10:34:44 +0100368 mgcpcmd := f_get_mgcp_by_crit(MgcpExpectTable[i].crit);
369 if (match(cmd, mgcpcmd)) {
370 ret := MgcpExpectTable[i].vc_conn;
Daniel Willmannfa870f52018-01-17 12:37:14 +0100371 /* Release this entry */
Harald Weltebb5a1212018-01-26 10:34:44 +0100372 MgcpExpectTable[i].crit := omit;
373 MgcpExpectTable[i].vc_conn := null;
Daniel Willmannfa870f52018-01-17 12:37:14 +0100374 log("Found Expect[", i, "] for ", cmd, " handled at ", ret);
375 return ret;
Harald Weltebb5a1212018-01-26 10:34:44 +0100376 }
Daniel Willmannfa870f52018-01-17 12:37:14 +0100377 }
378 setverdict(fail, "Couldn't find Expect for CRCX", cmd);
379 return ret;
380}
381
382private function f_create_expect(ExpectCriteria crit, MGCP_ConnHdlr hdlr)
383runs on MGCP_Emulation_CT {
384 var integer i;
385
386 /* Check an entry like this is not already presnt */
Harald Weltebb5a1212018-01-26 10:34:44 +0100387 for (i := 0; i < sizeof(MgcpExpectTable); i := i+1) {
388 if (crit == MgcpExpectTable[i].crit) {
Daniel Willmannfa870f52018-01-17 12:37:14 +0100389 setverdict(fail, "Crit already present", crit);
390 self.stop;
391 }
392 }
Harald Weltebb5a1212018-01-26 10:34:44 +0100393 for (i := 0; i < sizeof(MgcpExpectTable); i := i+1) {
394 if (not ispresent(MgcpExpectTable[i].crit)) {
395 MgcpExpectTable[i].crit := crit;
396 MgcpExpectTable[i].vc_conn := hdlr;
Daniel Willmannfa870f52018-01-17 12:37:14 +0100397 log("Created Expect[", i, "] for ", crit, " to be handled at ", hdlr);
398 return;
399 }
400 }
Harald Weltebb5a1212018-01-26 10:34:44 +0100401 setverdict(fail, "No space left in MgcpExpectTable")
402}
403
404/* client/conn_hdlr side function to use procedure port to create expect in emulation */
405function f_create_mgcp_expect(ExpectCriteria dest_number) runs on MGCP_ConnHdlr {
406 MGCP_PROC.call(MGCPEM_register:{dest_number, self}) {
407 [] MGCP_PROC.getreply(MGCPEM_register:{?,?}) {};
408 }
Daniel Willmannfa870f52018-01-17 12:37:14 +0100409}
410
Harald Welte9601c812018-01-26 18:58:20 +0100411/* client/conn_hdlr side function to use procedure port to create expect in emulation */
412function f_create_mgcp_delete_ep(MgcpEndpoint ep) runs on MGCP_ConnHdlr {
413 MGCP_PROC.call(MGCPEM_delete_ep:{ep, self}) {
414 [] MGCP_PROC.getreply(MGCPEM_delete_ep:{?,?}) {};
415 }
416}
417
418
Daniel Willmann955627a2018-01-17 15:22:32 +0100419private function f_expect_table_init()
420runs on MGCP_Emulation_CT {
421 var integer i;
Harald Weltebb5a1212018-01-26 10:34:44 +0100422 for (i := 0; i < sizeof(MgcpExpectTable); i := i + 1) {
423 MgcpExpectTable[i].crit := omit;
Daniel Willmann955627a2018-01-17 15:22:32 +0100424 }
425}
426
Harald Weltebb5a1212018-01-26 10:34:44 +0100427function DummyUnitdataCallback(MgcpMessage msg)
428runs on MGCP_Emulation_CT return template MgcpMessage {
429 log("Ignoring MGCP ", msg);
430 return omit;
431}
432
433
Daniel Willmannfa870f52018-01-17 12:37:14 +0100434}