blob: 31675799523bc0396a10832a23b2632a151f04a7 [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.
Harald Welte34b5a952019-05-27 11:54:11 +020028 *
29 * SPDX-License-Identifier: GPL-2.0-or-later
Harald Weltea02515f2018-01-26 10:33:23 +010030 */
31
Daniel Willmannfa870f52018-01-17 12:37:14 +010032import from MGCP_CodecPort all;
33import from MGCP_CodecPort_CtrlFunct all;
34import from MGCP_Types all;
35import from MGCP_Templates all;
36import from Osmocom_Types all;
37import from IPL4asp_Types all;
38
39type component MGCP_ConnHdlr {
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +020040 /* Simple send/recv without caring about peer addr+port. Used with multi_conn_mode=false. */
Daniel Willmannfa870f52018-01-17 12:37:14 +010041 port MGCP_Conn_PT MGCP;
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +020042 /* Handle multiple connections concurrently. Used with multi_conn_mode=true. */
43 port MGCP_Conn_Multi_PT MGCP_MULTI;
44 /* procedure based port to register for incoming connections. */
Harald Weltebb5a1212018-01-26 10:34:44 +010045 port MGCPEM_PROC_PT MGCP_PROC;
Daniel Willmannfa870f52018-01-17 12:37:14 +010046}
47
48/* port between individual per-connection components and this dispatcher */
49type port MGCP_Conn_PT message {
50 inout MgcpCommand, MgcpResponse;
51} with { extension "internal" };
52
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +020053/* port between individual per-connection components and this dispatcher */
54type port MGCP_Conn_Multi_PT message {
55 inout MGCP_RecvFrom, MGCP_SendTo;
56} with { extension "internal" };
57
Harald Welte9601c812018-01-26 18:58:20 +010058/* represents a single MGCP Endpoint */
59type record EndpointData {
Harald Weltebb5a1212018-01-26 10:34:44 +010060 MGCP_ConnHdlr comp_ref,
Harald Welte9601c812018-01-26 18:58:20 +010061 MgcpEndpoint endpoint optional
Harald Weltebb5a1212018-01-26 10:34:44 +010062};
Daniel Willmannfa870f52018-01-17 12:37:14 +010063
Harald Welte9601c812018-01-26 18:58:20 +010064/* pending CRCX with their transaction ID */
65type set of MgcpTransId MgcpTransIds;
66
Daniel Willmannfa870f52018-01-17 12:37:14 +010067type component MGCP_Emulation_CT {
68 /* Port facing to the UDP SUT */
69 port MGCP_CODEC_PT MGCP;
70 /* All MGCP_ConnHdlr MGCP ports connect here
71 * MGCP_Emulation_CT.main needs to figure out what messages
72 * to send where with CLIENT.send() to vc_conn */
Harald Weltebb5a1212018-01-26 10:34:44 +010073 port MGCP_Conn_PT MGCP_CLIENT;
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +020074 /* This one is used with multi_conn_mode=true and allows differentiating UDP sockets */
75 port MGCP_Conn_Multi_PT MGCP_CLIENT_MULTI;
Daniel Willmannfa870f52018-01-17 12:37:14 +010076 /* currently tracked connections */
Harald Welte9601c812018-01-26 18:58:20 +010077 var EndpointData MgcpEndpointTable[16];
78 var MgcpTransIds MgcpPendingTrans := {};
Daniel Willmannfa870f52018-01-17 12:37:14 +010079 /* pending expected CRCX */
Harald Weltebb5a1212018-01-26 10:34:44 +010080 var ExpectData MgcpExpectTable[8];
Daniel Willmannfa870f52018-01-17 12:37:14 +010081 /* procedure based port to register for incoming connections */
Harald Weltebb5a1212018-01-26 10:34:44 +010082 port MGCPEM_PROC_PT MGCP_PROC;
Daniel Willmannfa870f52018-01-17 12:37:14 +010083
84 var charstring g_mgcp_id;
Daniel Willmann166bbb32018-01-17 15:28:04 +010085 var integer g_mgcp_conn_id := -1;
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +020086
87 var MGCP_conn_parameters g_pars;
Daniel Willmannfa870f52018-01-17 12:37:14 +010088}
89
90type function MGCPCreateCallback(MgcpCommand cmd, charstring id)
91runs on MGCP_Emulation_CT return MGCP_ConnHdlr;
92
Harald Weltebb5a1212018-01-26 10:34:44 +010093type function MGCPUnitdataCallback(MgcpMessage msg)
94runs on MGCP_Emulation_CT return template MgcpMessage;
95
Daniel Willmannfa870f52018-01-17 12:37:14 +010096type record MGCPOps {
Harald Weltebb5a1212018-01-26 10:34:44 +010097 MGCPCreateCallback create_cb,
98 MGCPUnitdataCallback unitdata_cb
Daniel Willmannfa870f52018-01-17 12:37:14 +010099}
100
101type record MGCP_conn_parameters {
Harald Weltebb5a1212018-01-26 10:34:44 +0100102 HostName callagent_ip,
103 PortNumber callagent_udp_port,
104 HostName mgw_ip,
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200105 PortNumber mgw_udp_port,
106 boolean multi_conn_mode
Daniel Willmannfa870f52018-01-17 12:37:14 +0100107}
108
Daniel Willmann166bbb32018-01-17 15:28:04 +0100109function tr_MGCP_RecvFrom_R(template MgcpMessage msg)
110runs on MGCP_Emulation_CT return template MGCP_RecvFrom {
111 var template MGCP_RecvFrom mrf := {
112 connId := g_mgcp_conn_id,
113 remName := ?,
114 remPort := ?,
115 locName := ?,
116 locPort := ?,
117 msg := msg
118 }
119 return mrf;
120}
121
Harald Welte9601c812018-01-26 18:58:20 +0100122private function f_ep_known(MgcpEndpoint ep)
Harald Weltebb5a1212018-01-26 10:34:44 +0100123runs on MGCP_Emulation_CT return boolean {
124 var integer i;
Harald Welte9601c812018-01-26 18:58:20 +0100125 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
126 if (MgcpEndpointTable[i].endpoint == ep) {
Harald Weltebb5a1212018-01-26 10:34:44 +0100127 return true;
128 }
129 }
130 return false;
131}
132
133private function f_comp_known(MGCP_ConnHdlr client)
134runs on MGCP_Emulation_CT return boolean {
135 var integer i;
Harald Welte9601c812018-01-26 18:58:20 +0100136 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
137 if (MgcpEndpointTable[i].comp_ref == client) {
Harald Weltebb5a1212018-01-26 10:34:44 +0100138 return true;
139 }
140 }
141 return false;
142}
143
Harald Welte9601c812018-01-26 18:58:20 +0100144private function f_comp_by_ep(MgcpEndpoint ep)
Harald Weltebb5a1212018-01-26 10:34:44 +0100145runs on MGCP_Emulation_CT return MGCP_ConnHdlr {
146 var integer i;
Harald Welte9601c812018-01-26 18:58:20 +0100147 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
148 if (MgcpEndpointTable[i].endpoint == ep) {
149 return MgcpEndpointTable[i].comp_ref;
Harald Weltebb5a1212018-01-26 10:34:44 +0100150 }
151 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200152 setverdict(fail, "MGCP Endpoint Table not found by Endpoint", ep);
153 mtc.stop;
Harald Weltebb5a1212018-01-26 10:34:44 +0100154}
155
Harald Welte9601c812018-01-26 18:58:20 +0100156private function f_ep_by_comp(MGCP_ConnHdlr client)
157runs on MGCP_Emulation_CT return MgcpEndpoint {
Harald Weltebb5a1212018-01-26 10:34:44 +0100158 var integer i;
Harald Welte9601c812018-01-26 18:58:20 +0100159 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
160 if (MgcpEndpointTable[i].comp_ref == client) {
161 return MgcpEndpointTable[i].endpoint;
Harald Weltebb5a1212018-01-26 10:34:44 +0100162 }
163 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200164 setverdict(fail, "MGCP Endpoint Table not found by component ", client);
165 mtc.stop;
Harald Weltebb5a1212018-01-26 10:34:44 +0100166}
167
Harald Welte9601c812018-01-26 18:58:20 +0100168private function f_ep_table_add(MGCP_ConnHdlr comp_ref, MgcpEndpoint ep)
169runs on MGCP_Emulation_CT {
170 var integer i;
171 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
172 if (not isvalue(MgcpEndpointTable[i].endpoint)) {
173 MgcpEndpointTable[i].endpoint := ep;
174 MgcpEndpointTable[i].comp_ref := comp_ref;
175 return;
176 }
177 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200178 testcase.stop("MGCP Endpoint Table full!");
Harald Welte9601c812018-01-26 18:58:20 +0100179}
180
181private function f_ep_table_del(MGCP_ConnHdlr comp_ref, MgcpEndpoint ep)
182runs on MGCP_Emulation_CT {
183 var integer i;
184 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
185 if (MgcpEndpointTable[i].comp_ref == comp_ref and
186 MgcpEndpointTable[i].endpoint == ep) {
187 MgcpEndpointTable[i].endpoint := omit;
188 MgcpEndpointTable[i].comp_ref := null;
189 return;
190 }
191 }
192 setverdict(fail, "MGCP Endpoint Table: Couldn't find to-be-deleted entry!");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200193 mtc.stop;
Harald Welte9601c812018-01-26 18:58:20 +0100194}
195
Neels Hofmeyr5c449f32021-07-27 22:30:36 +0200196private function f_ep_table_change_connhdlr(MGCP_ConnHdlr comp_ref, MgcpEndpoint ep)
197runs on MGCP_Emulation_CT {
198 var integer i;
199 for (i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
200 if (MgcpEndpointTable[i].endpoint == ep) {
201 MgcpEndpointTable[i].comp_ref := comp_ref;
202 log("MGCP_Emulation_CT: MgcpEndpointTable[", i, "] now sends to ", comp_ref);
203 return;
204 }
205 }
206 setverdict(fail, "MGCP Endpoint Table: Couldn't find entry to move to ", comp_ref);
207 mtc.stop;
208}
Harald Welte9601c812018-01-26 18:58:20 +0100209
210/* Check if the given transaction ID is a pending CRCX. If yes, return true + remove */
211private function f_trans_id_was_pending(MgcpTransId trans_id)
212runs on MGCP_Emulation_CT return boolean {
213 for (var integer i := 0; i < lengthof(MgcpPendingTrans); i := i+1) {
214 if (MgcpPendingTrans[i] == trans_id) {
215 /* Remove from list */
216 var MgcpTransIds OldPendingTrans := MgcpPendingTrans;
217 MgcpPendingTrans := {}
218 for (var integer j := 0; j < lengthof(OldPendingTrans); j := j+1) {
219 if (j != i) {
220 MgcpPendingTrans := MgcpPendingTrans & {OldPendingTrans[j]};
221 }
222 }
223 return true;
224 }
225 }
226 return false;
227}
228
Harald Weltebb5a1212018-01-26 10:34:44 +0100229/* TODO: move this to MGCP_Types? */
Harald Welte9601c812018-01-26 18:58:20 +0100230function f_mgcp_ep(MgcpMessage msg) return MgcpEndpoint {
Harald Weltebb5a1212018-01-26 10:34:44 +0100231 var MgcpParameterList params;
232 var integer i;
233 if (ischosen(msg.command)) {
Harald Welte9601c812018-01-26 18:58:20 +0100234 return msg.command.line.ep;
Harald Weltebb5a1212018-01-26 10:34:44 +0100235 } else {
Harald Welte363cb0a2018-01-30 19:35:53 +0100236 var MgcpEndpoint ep;
237 if (f_mgcp_find_param(msg, "Z", ep) == false) {
238 setverdict(fail, "No SpecificEndpointName in MGCP response", msg);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200239 mtc.stop;
Harald Welte363cb0a2018-01-30 19:35:53 +0100240 }
241 return ep;
Harald Weltebb5a1212018-01-26 10:34:44 +0100242 }
Harald Weltebb5a1212018-01-26 10:34:44 +0100243}
244
Harald Welte9601c812018-01-26 18:58:20 +0100245private function f_ep_table_init()
Harald Weltebb5a1212018-01-26 10:34:44 +0100246runs on MGCP_Emulation_CT {
Harald Welte9601c812018-01-26 18:58:20 +0100247 for (var integer i := 0; i < sizeof(MgcpEndpointTable); i := i+1) {
248 MgcpEndpointTable[i].comp_ref := null;
249 MgcpEndpointTable[i].endpoint := omit;
Harald Weltebb5a1212018-01-26 10:34:44 +0100250 }
251}
252
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200253private function f_forward_to_client(MGCP_RecvFrom mrf, MGCP_ConnHdlr vc_conn) runs on MGCP_Emulation_CT {
254 if (g_pars.multi_conn_mode) {
255 MGCP_CLIENT_MULTI.send(mrf) to vc_conn;
256 } else {
257 MGCP_CLIENT.send(mrf.msg.command) to vc_conn;
258 }
259}
260
Daniel Willmann955627a2018-01-17 15:22:32 +0100261function main(MGCPOps ops, MGCP_conn_parameters p, charstring id) runs on MGCP_Emulation_CT {
Daniel Willmannfa870f52018-01-17 12:37:14 +0100262 var Result res;
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200263 g_pars := p;
Daniel Willmannfa870f52018-01-17 12:37:14 +0100264 g_mgcp_id := id;
Harald Welte9601c812018-01-26 18:58:20 +0100265 f_ep_table_init();
Daniel Willmann955627a2018-01-17 15:22:32 +0100266 f_expect_table_init();
Daniel Willmannfa870f52018-01-17 12:37:14 +0100267
268 map(self:MGCP, system:MGCP_CODEC_PT);
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200269 if (p.multi_conn_mode or p.callagent_udp_port == -1) {
Harald Weltebb5a1212018-01-26 10:34:44 +0100270 res := MGCP_CodecPort_CtrlFunct.f_IPL4_listen(MGCP, p.mgw_ip, p.mgw_udp_port, { udp:={} });
271 } else {
272 res := MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, p.callagent_ip, p.callagent_udp_port, p.mgw_ip, p.mgw_udp_port, -1, { udp:={} });
273 }
Harald Welte9220f632018-05-23 20:27:02 +0200274 if (not ispresent(res.connId)) {
275 setverdict(fail, "Could not connect MGCP socket, check your configuration");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200276 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200277 }
Daniel Willmann166bbb32018-01-17 15:28:04 +0100278 g_mgcp_conn_id := res.connId;
Harald Weltebb5a1212018-01-26 10:34:44 +0100279
Daniel Willmannfa870f52018-01-17 12:37:14 +0100280 while (true) {
Daniel Willmann166bbb32018-01-17 15:28:04 +0100281 var MGCP_ConnHdlr vc_conn;
282 var ExpectCriteria crit;
283 var MGCP_RecvFrom mrf;
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200284 var MGCP_SendTo mst;
Daniel Willmann166bbb32018-01-17 15:28:04 +0100285 var MgcpMessage msg;
286 var MgcpCommand cmd;
287 var MgcpResponse resp;
Harald Welte9601c812018-01-26 18:58:20 +0100288 var MgcpEndpoint ep;
Daniel Willmann166bbb32018-01-17 15:28:04 +0100289
Daniel Willmannfa870f52018-01-17 12:37:14 +0100290 alt {
Daniel Willmann166bbb32018-01-17 15:28:04 +0100291 /* MGCP from client */
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200292 [not p.multi_conn_mode] MGCP_CLIENT.receive(MgcpResponse:?) -> value resp sender vc_conn {
Harald Welte9601c812018-01-26 18:58:20 +0100293 msg := {
294 response := resp
295 };
Oliver Smith23e192e2023-02-13 15:00:46 +0100296 /* If this is the response to a pending CRCX, extract Endpoint and store in table */
Harald Welte9601c812018-01-26 18:58:20 +0100297 if (f_trans_id_was_pending(resp.line.trans_id)) {
298 f_ep_table_add(vc_conn, f_mgcp_ep(msg));
299 }
Daniel Willmann166bbb32018-01-17 15:28:04 +0100300 /* Pass message through */
Harald Weltebb5a1212018-01-26 10:34:44 +0100301 /* TODO: check which ConnectionID client has allocated + store in table? */
Daniel Willmann166bbb32018-01-17 15:28:04 +0100302 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Daniel Willmannfa870f52018-01-17 12:37:14 +0100303 }
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200304
305 /* MGCP from client in Multi Conn mode */
306 [p.multi_conn_mode] MGCP_CLIENT_MULTI.receive(MGCP_SendTo:?) -> value mst sender vc_conn {
Oliver Smith23e192e2023-02-13 15:00:46 +0100307 /* If this is the response to a pending CRCX, extract Endpoint and store in table */
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200308 if (f_trans_id_was_pending(mst.msg.response.line.trans_id)) {
309 f_ep_table_add(vc_conn, f_mgcp_ep(mst.msg));
310 }
311 /* Pass message through */
312 /* TODO: check which ConnectionID client has allocated + store in table? */
313 MGCP.send(mst);
314 }
Daniel Willmann166bbb32018-01-17 15:28:04 +0100315 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) -> value mrf {
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200316 if (not p.multi_conn_mode and p.callagent_udp_port == -1) {
317 /* we aren't yet connected to the remote side
318 port, let's fix this. This way upper layers
319 can use Send/Recv without caring about UDP
320 src/dst addr + port */
Harald Weltebb5a1212018-01-26 10:34:44 +0100321 p.callagent_udp_port := mrf.remPort;
Harald Welte930d0a72018-03-22 22:08:40 +0100322 res := 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:={} });
Harald Welte9220f632018-05-23 20:27:02 +0200323 if (not ispresent(res.connId)) {
324 setverdict(fail, "Could not connect MGCP socket, check your configuration");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200325 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200326 }
Harald Weltebb5a1212018-01-26 10:34:44 +0100327 }
Daniel Willmann166bbb32018-01-17 15:28:04 +0100328 if (ischosen(mrf.msg.command)) {
329 cmd := mrf.msg.command;
Harald Welte9601c812018-01-26 18:58:20 +0100330 if (f_ep_known(cmd.line.ep)) {
331 vc_conn := f_comp_by_ep(cmd.line.ep);
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200332 f_forward_to_client(mrf, vc_conn);
Harald Weltebb5a1212018-01-26 10:34:44 +0100333 } else {
Harald Welte9601c812018-01-26 18:58:20 +0100334 if (cmd.line.verb == "CRCX") {
335 vc_conn := ops.create_cb.apply(cmd, id);
Harald Welte363cb0a2018-01-30 19:35:53 +0100336 if (not match(cmd.line.ep, t_MGCP_EP_wildcard)) {
337 /* non-wildcard EP, use directly */
Harald Welte9601c812018-01-26 18:58:20 +0100338 f_ep_table_add(vc_conn, cmd.line.ep);
339 } else {
340 /* add this transaction to list of pending transactions */
341 MgcpPendingTrans := MgcpPendingTrans & {cmd.line.trans_id};
342 }
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200343 f_forward_to_client(mrf, vc_conn);
Harald Welte9601c812018-01-26 18:58:20 +0100344 } else {
345 /* connectionless MGCP, i.e. messages without ConnectionId */
346 var template MgcpMessage r := ops.unitdata_cb.apply(mrf.msg);
347 if (isvalue(r)) {
Pau Espin Pedrol1a026a52019-06-18 17:21:52 +0200348 MGCP.send(t_MGCP_SendToMrf(mrf, r));
Harald Welte9601c812018-01-26 18:58:20 +0100349 }
Harald Weltebb5a1212018-01-26 10:34:44 +0100350 }
351 }
Daniel Willmann166bbb32018-01-17 15:28:04 +0100352 } else {
353 setverdict(fail, "Received unexpected MGCP response: ", mrf.msg.response);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200354 mtc.stop;
Daniel Willmann166bbb32018-01-17 15:28:04 +0100355 }
Daniel Willmannfa870f52018-01-17 12:37:14 +0100356 }
Harald Weltebb5a1212018-01-26 10:34:44 +0100357 [] MGCP_PROC.getcall(MGCPEM_register:{?,?}) -> param(crit, vc_conn) {
Daniel Willmann955627a2018-01-17 15:22:32 +0100358 f_create_expect(crit, vc_conn);
Harald Weltee32ad992018-05-31 22:17:46 +0200359 MGCP_PROC.reply(MGCPEM_register:{crit, vc_conn}) to vc_conn;
Daniel Willmann955627a2018-01-17 15:22:32 +0100360 }
Harald Welte9601c812018-01-26 18:58:20 +0100361 [] MGCP_PROC.getcall(MGCPEM_delete_ep:{?,?}) -> param(ep, vc_conn) {
362 f_ep_table_del(vc_conn, ep);
Harald Weltee32ad992018-05-31 22:17:46 +0200363 MGCP_PROC.reply(MGCPEM_delete_ep:{ep, vc_conn}) to vc_conn;
Harald Welte9601c812018-01-26 18:58:20 +0100364 }
Neels Hofmeyr5c449f32021-07-27 22:30:36 +0200365 [] MGCP_PROC.getcall(MGCPEM_change_connhdlr:{?,?}) -> param(ep, vc_conn) {
366 f_ep_table_change_connhdlr(vc_conn, ep);
367 MGCP_PROC.reply(MGCPEM_change_connhdlr:{ep, vc_conn}) to vc_conn;
368 }
Daniel Willmannfa870f52018-01-17 12:37:14 +0100369 }
Harald Welte9601c812018-01-26 18:58:20 +0100370
Daniel Willmannfa870f52018-01-17 12:37:14 +0100371 }
372}
373
374/* "Expect" Handling */
375
376/* */
Daniel Willmann955627a2018-01-17 15:22:32 +0100377type record ExpectCriteria {
378 MgcpConnectionId connid optional,
379 MgcpEndpoint endpoint optional,
380 MgcpTransId transid optional
Daniel Willmannfa870f52018-01-17 12:37:14 +0100381}
382
383type record ExpectData {
384 ExpectCriteria crit optional,
Daniel Willmann955627a2018-01-17 15:22:32 +0100385 MGCP_ConnHdlr vc_conn
Daniel Willmannfa870f52018-01-17 12:37:14 +0100386}
387
Daniel Willmann955627a2018-01-17 15:22:32 +0100388signature MGCPEM_register(in ExpectCriteria cmd, in MGCP_ConnHdlr hdlr);
Harald Welte9601c812018-01-26 18:58:20 +0100389signature MGCPEM_delete_ep(in MgcpEndpoint ep, in MGCP_ConnHdlr hdlr);
Neels Hofmeyr5c449f32021-07-27 22:30:36 +0200390signature MGCPEM_change_connhdlr(in MgcpEndpoint ep, in MGCP_ConnHdlr hdlr);
Daniel Willmannfa870f52018-01-17 12:37:14 +0100391
392type port MGCPEM_PROC_PT procedure {
Neels Hofmeyr5c449f32021-07-27 22:30:36 +0200393 inout MGCPEM_register, MGCPEM_delete_ep, MGCPEM_change_connhdlr;
Daniel Willmannfa870f52018-01-17 12:37:14 +0100394} with { extension "internal" };
395
396function f_get_mgcp_by_crit(ExpectCriteria crit)
397return template MgcpCommand {
Harald Weltebb5a1212018-01-26 10:34:44 +0100398 var template MgcpCommand ret := {
399 line := {
400 verb := ?,
401 trans_id := ?,
402 ep := ?,
403 ver := ?
404 },
405 params := *,
406 sdp := *
407 }
408 if (ispresent(crit.connid)) {
409 ret.params := { *, ts_MgcpParConnectionId(crit.connid), * };
410 }
411 if (ispresent(crit.endpoint)) {
412 ret.line.ep := crit.endpoint;
413 }
414 if (ispresent(crit.transid)) {
415 ret.line.trans_id := crit.transid;
416 }
Daniel Willmannfa870f52018-01-17 12:37:14 +0100417
418 return ret;
419}
420
Oliver Smith23e192e2023-02-13 15:00:46 +0100421/* Function that can be used as create_cb and will use the expect table */
Daniel Willmannfa870f52018-01-17 12:37:14 +0100422function ExpectedCreateCallback(MgcpCommand cmd, charstring id)
423runs on MGCP_Emulation_CT return MGCP_ConnHdlr {
424 var MGCP_ConnHdlr ret := null;
425 var template MgcpCommand mgcpcmd;
426 var integer i;
427
Harald Weltebb5a1212018-01-26 10:34:44 +0100428 for (i := 0; i < sizeof(MgcpExpectTable); i := i+1) {
429 if (not ispresent(MgcpExpectTable[i].crit)) {
Daniel Willmannfa870f52018-01-17 12:37:14 +0100430 continue;
431 }
Daniel Willmann955627a2018-01-17 15:22:32 +0100432 /* FIXME: Ignore criteria for now */
Harald Weltebb5a1212018-01-26 10:34:44 +0100433 mgcpcmd := f_get_mgcp_by_crit(MgcpExpectTable[i].crit);
434 if (match(cmd, mgcpcmd)) {
435 ret := MgcpExpectTable[i].vc_conn;
Daniel Willmannfa870f52018-01-17 12:37:14 +0100436 /* Release this entry */
Harald Weltebb5a1212018-01-26 10:34:44 +0100437 MgcpExpectTable[i].crit := omit;
438 MgcpExpectTable[i].vc_conn := null;
Daniel Willmannfa870f52018-01-17 12:37:14 +0100439 log("Found Expect[", i, "] for ", cmd, " handled at ", ret);
440 return ret;
Harald Weltebb5a1212018-01-26 10:34:44 +0100441 }
Daniel Willmannfa870f52018-01-17 12:37:14 +0100442 }
443 setverdict(fail, "Couldn't find Expect for CRCX", cmd);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200444 mtc.stop;
Daniel Willmannfa870f52018-01-17 12:37:14 +0100445}
446
447private function f_create_expect(ExpectCriteria crit, MGCP_ConnHdlr hdlr)
448runs on MGCP_Emulation_CT {
449 var integer i;
450
Oliver Smith136d4642023-05-08 12:36:46 +0200451 /* Check an entry like this is not already present */
Harald Weltebb5a1212018-01-26 10:34:44 +0100452 for (i := 0; i < sizeof(MgcpExpectTable); i := i+1) {
453 if (crit == MgcpExpectTable[i].crit) {
Daniel Willmannfa870f52018-01-17 12:37:14 +0100454 setverdict(fail, "Crit already present", crit);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200455 mtc.stop;
Daniel Willmannfa870f52018-01-17 12:37:14 +0100456 }
457 }
Harald Weltebb5a1212018-01-26 10:34:44 +0100458 for (i := 0; i < sizeof(MgcpExpectTable); i := i+1) {
459 if (not ispresent(MgcpExpectTable[i].crit)) {
460 MgcpExpectTable[i].crit := crit;
461 MgcpExpectTable[i].vc_conn := hdlr;
Daniel Willmannfa870f52018-01-17 12:37:14 +0100462 log("Created Expect[", i, "] for ", crit, " to be handled at ", hdlr);
463 return;
464 }
465 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200466 testcase.stop("No space left in MgcpExpectTable")
Harald Weltebb5a1212018-01-26 10:34:44 +0100467}
468
469/* client/conn_hdlr side function to use procedure port to create expect in emulation */
470function f_create_mgcp_expect(ExpectCriteria dest_number) runs on MGCP_ConnHdlr {
471 MGCP_PROC.call(MGCPEM_register:{dest_number, self}) {
472 [] MGCP_PROC.getreply(MGCPEM_register:{?,?}) {};
473 }
Daniel Willmannfa870f52018-01-17 12:37:14 +0100474}
475
Vadim Yanitskiyc8334652021-02-05 01:52:20 +0100476/* client/conn_hdlr side function to use procedure port to delete expect in emulation */
Harald Welte9601c812018-01-26 18:58:20 +0100477function f_create_mgcp_delete_ep(MgcpEndpoint ep) runs on MGCP_ConnHdlr {
478 MGCP_PROC.call(MGCPEM_delete_ep:{ep, self}) {
479 [] MGCP_PROC.getreply(MGCPEM_delete_ep:{?,?}) {};
480 }
481}
482
Neels Hofmeyr5c449f32021-07-27 22:30:36 +0200483/* Move MGCP handling for a given MGW endpoint to a different MGCP_ConnHdlr component. */
484function f_mgcp_change_connhdlr(MgcpEndpoint ep) runs on MGCP_ConnHdlr {
485 MGCP_PROC.call(MGCPEM_change_connhdlr:{ep, self}) {
486 [] MGCP_PROC.getreply(MGCPEM_change_connhdlr:{?,?}) {};
487 }
488}
489
Harald Welte9601c812018-01-26 18:58:20 +0100490
Daniel Willmann955627a2018-01-17 15:22:32 +0100491private function f_expect_table_init()
492runs on MGCP_Emulation_CT {
493 var integer i;
Harald Weltebb5a1212018-01-26 10:34:44 +0100494 for (i := 0; i < sizeof(MgcpExpectTable); i := i + 1) {
495 MgcpExpectTable[i].crit := omit;
Daniel Willmann955627a2018-01-17 15:22:32 +0100496 }
497}
498
Harald Weltebb5a1212018-01-26 10:34:44 +0100499function DummyUnitdataCallback(MgcpMessage msg)
500runs on MGCP_Emulation_CT return template MgcpMessage {
501 log("Ignoring MGCP ", msg);
502 return omit;
503}
504
Philipp Maier11a58942018-06-25 16:40:48 +0200505/* Determine encoding name for a specified payload type number */
506function f_encoding_name_from_pt(SDP_FIELD_PayloadType pt) return charstring {
507 if (pt == PT_PCMU) {
508 return "PCMU";
509 } else if (pt == PT_GSM) {
510 return "GSM";
511 } else if (pt == PT_PCMA) {
512 return "PCMA";
513 } else if (pt == PT_GSMEFR) {
514 return "GSM-EFR";
515 } else if (pt == PT_GSMHR) {
516 return "GSM-HR-08";
517 } else if (pt == PT_AMR) {
518 return "AMR";
519 } else if (pt == PT_AMRWB) {
520 return "AMR-WB";
521 }
522
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200523 setverdict(fail, "Unknown payload type ", pt);
524 mtc.stop;
Philipp Maier11a58942018-06-25 16:40:48 +0200525}
Harald Weltebb5a1212018-01-26 10:34:44 +0100526
Daniel Willmannfa870f52018-01-17 12:37:14 +0100527}