blob: 72528823745e242e8a1ca06161342e7cac1dd21a [file] [log] [blame]
Harald Welte34b5a952019-05-27 11:54:11 +02001/* MGW (Media Gateway) test suite in TTCN-3
2 * (C) 2017-2018 Harald Welte <laforge@gnumonks.org>
3 * (C) 2018-2019 sysmocom - s.f.m.c. GmbH
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
Harald Welte00a067f2017-09-13 23:27:17 +020012module MGCP_Test {
Harald Weltef07c2862017-11-18 17:16:24 +010013 import from Osmocom_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +020014 import from MGCP_Types all;
Harald Welte4029e8c2017-11-23 22:00:42 +010015 import from MGCP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080016 import from SDP_Types all;
17 import from MGCP_CodecPort all;
18 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +010019 import from RTP_CodecPort all;
20 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020021 import from RTP_Emulation all;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +020022 import from IuUP_Emulation all;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020023 import from OSMUX_Types all;
24 import from OSMUX_CodecPort all;
25 import from OSMUX_CodecPort_CtrlFunct all;
26 import from OSMUX_Emulation all;
Pau Espin Pedrol71ed4632022-09-02 18:51:19 +020027 import from AMR_Types all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080028 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010029 import from General_Types all;
30 import from Native_Functions all;
31 import from IPCP_Types all;
32 import from IP_Types all;
33 import from Osmocom_VTY_Functions all;
34 import from TELNETasp_PortType all;
Philipp Maier55b90542021-07-02 12:33:19 +020035 import from StatsD_Types all;
36 import from StatsD_CodecPort all;
37 import from StatsD_CodecPort_CtrlFunct all;
38 import from StatsD_Checker all;
Philipp Maier3560bd62021-08-19 11:52:23 +020039 import from Osmocom_CTRL_Functions all;
40 import from Osmocom_CTRL_Types all;
41 import from Osmocom_CTRL_Adapter all;
Harald Welte00a067f2017-09-13 23:27:17 +020042
Philipp Maierbb7a01c2018-02-01 12:32:57 +010043 const charstring c_mgw_domain := "mgw";
44 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
45
Harald Welte21ba5572017-09-19 17:55:05 +080046 /* any variables declared in the component will be available to
47 * all functions that 'run on' the named component, similar to
48 * class members in C++ */
Philipp Maier3560bd62021-08-19 11:52:23 +020049 type component dummy_CT extends StatsD_ConnHdlr, CTRL_Adapter_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080050 port MGCP_CODEC_PT MGCP;
51 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010052 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080053 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010054
Philipp Maier2321ef92018-06-27 17:52:04 +020055 var RTP_Emulation_CT vc_RTPEM[3];
56 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010057
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020058 var OSMUX_Emulation_CT vc_OsmuxEM;
59 port OsmuxEM_CTRL_PT OsmuxEM;
60
Philipp Maier6137c322019-02-20 16:13:41 +010061 port TELNETasp_PT MGWVTY;
Philipp Maier55b90542021-07-02 12:33:19 +020062
63 var StatsD_Checker_CT vc_STATSD;
Harald Welte00a067f2017-09-13 23:27:17 +020064 };
65
Harald Weltee1e18c52017-09-17 16:23:07 +080066 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
67 var MgcpTransId tid := int2str(g_trans_id);
68 g_trans_id := g_trans_id + 1;
69 return tid;
70 }
71
Harald Welte21ba5572017-09-19 17:55:05 +080072 /* all parameters declared here can be modified / overridden by
73 * the config file in the [MODULE_PARAMETERS] section. If no
74 * config file is used or the file doesn't specify them, the
75 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080076 modulepar {
77 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020078 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020079 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080080 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020081 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020082 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010083 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020084 PortNumber mp_local_osmux_port := 1985;
Philipp Maier55b90542021-07-02 12:33:19 +020085 PortNumber mp_mgw_statsd_port := 8125;
Philipp Maier3560bd62021-08-19 11:52:23 +020086 PortNumber mp_mgw_ctrl_port := 4267;
Pau Espin Pedrole7928872022-10-07 10:57:11 +020087 /* Maximum number of available endpoints in osmo-mgw.cfg ("number endpoints"): */
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +020088 integer mp_num_endpoints := 300;
Harald Welte3c6ebb92017-09-16 00:56:57 +080089 }
90
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020091 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
92 /* Turn on conversion mode */
93 f_vty_enter_config(MGWVTY);
94 f_vty_transceive(MGWVTY, "mgcp");
95 if (osmux_on) {
96 f_vty_transceive(MGWVTY, "osmux on");
97 } else {
98 f_vty_transceive(MGWVTY, "osmux off");
99 }
100 f_vty_transceive(MGWVTY, "exit");
101 f_vty_transceive(MGWVTY, "exit");
102
103 }
104
105 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +0100106 map(self:MGWVTY, system:MGWVTY);
107 f_vty_set_prompts(MGWVTY);
108 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200109
110 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +0100111 }
112
Harald Weltebb7523b2018-03-29 08:52:01 +0200113 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
114 runs on dummy_CT {
115 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
116 map(comp_ref:RTP, system:RTP);
117 map(comp_ref:RTCP, system:RTCP);
118 comp_ref.start(RTP_Emulation.f_main());
119 }
120
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200121 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
122 runs on dummy_CT {
123 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
124 map(comp_ref:OSMUX, system:OSMUX);
125 comp_ref.start(OSMUX_Emulation.f_main());
126 }
127
Harald Welte21ba5572017-09-19 17:55:05 +0800128 /* initialization function, called by each test case at the
129 * beginning, but 'initialized' variable ensures its body is
130 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200131 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800132 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100133 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200134
Harald Welteedc45c12017-11-18 19:15:05 +0100135 if (initialized == false) {
136 initialized := true;
137
138 /* some random number for the initial transaction id */
139 g_trans_id := float2int(rnd()*65535.0);
140 map(self:MGCP, system:MGCP_CODEC_PT);
141 /* connect the MGCP test port using the given
142 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
143 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200144 res := MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, mp_remote_ipv4, mp_remote_udp_port, mp_local_ipv4, mp_local_udp_port, 0, { udp := {} });
Harald Welte9220f632018-05-23 20:27:02 +0200145 if (not ispresent(res.connId)) {
146 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200147 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200148 }
Harald Welteedc45c12017-11-18 19:15:05 +0100149 g_mgcp_conn_id := res.connId;
150
Harald Weltebb7523b2018-03-29 08:52:01 +0200151 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
152 f_rtpem_init(vc_RTPEM[i], i);
153 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
154 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200155 if (osmux_on) {
156 f_osmuxem_init(vc_OsmuxEM);
157 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
158 }
Philipp Maier55b90542021-07-02 12:33:19 +0200159
Philipp Maier2ff3e662021-08-19 10:52:33 +0200160 f_init_statsd("VirtCallAgent", vc_STATSD, mp_local_ipv4, mp_mgw_statsd_port);
Philipp Maier55b90542021-07-02 12:33:19 +0200161 connect(self:STATSD_PROC, vc_STATSD:STATSD_PROC);
162
163 f_statsd_reset();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800164 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800165
Philipp Maier3560bd62021-08-19 11:52:23 +0200166 f_ipa_ctrl_start_client(mp_remote_ipv4, mp_mgw_ctrl_port);
167
Harald Welteedc45c12017-11-18 19:15:05 +0100168 if (isvalue(ep)) {
169 /* do a DLCX on all connections of the EP */
170 f_dlcx_ignore(valueof(ep));
171 }
Philipp Maier6137c322019-02-20 16:13:41 +0100172
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200173 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800174 }
175
Harald Welte00a067f2017-09-13 23:27:17 +0200176 testcase TC_selftest() runs on dummy_CT {
177 const charstring c_auep := "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100178 const charstring c_mdcx3 := "MDCX 18983215 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n";
Harald Welte00a067f2017-09-13 23:27:17 +0200179 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
180 "I: 1\n" &
181 "\n" &
182 "v=0\r\n" &
183 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
184 "s=-\r\n" &
185 "c=IN IP4 0.0.0.0\r\n" &
186 "t=0 0\r\n" &
187 "m=audio 0 RTP/AVP 126\r\n" &
188 "a=rtpmap:126 AMR/8000\r\n" &
189 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100190 const charstring c_mdcx4 := "MDCX 18983216 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200191 "M: sendrecv\r" &
192 "C: 2\r\n" &
193 "I: 1\r\n" &
194 "L: p:20, a:AMR, nt:IN\r\n" &
195 "\n" &
196 "v=0\r\n" &
197 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800198 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200199 "c=IN IP4 0.0.0.0\r\n" &
200 "t=0 0\r\n" &
201 "m=audio 4441 RTP/AVP 99\r\n" &
202 "a=rtpmap:99 AMR/8000\r\n" &
203 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800204 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200205
206 log(c_auep);
207 log(dec_MgcpCommand(c_auep));
208
209 log(c_mdcx3);
210 log(dec_MgcpCommand(c_mdcx3));
211
212 log(c_mdcx3_ret);
213 log(dec_MgcpResponse(c_mdcx3_ret));
214
215 log(c_mdcx4);
216 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800217
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100218 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
219 log(enc_MgcpCommand(valueof(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H))));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800220
221 log(c_crcx510_ret);
222 log(dec_MgcpResponse(c_crcx510_ret));
223 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100224
225 /* We didn't encounter any DTE, so pass the test */
226 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800227 }
228
Harald Weltee636afd2017-09-17 16:24:09 +0800229 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100230 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800231 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
232 * - CRCX with remote session description and without
233 *
234 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100235 * x packetization != 20ms
236 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800237 * x unsupported mode (517)
238 * x bidirectional mode before RemoteConnDesc: 527
239 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100240 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800241 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
242 */
243
Harald Welte21ba5572017-09-19 17:55:05 +0800244 /* build a receive template for receiving a MGCP message. You
245 * pass the MGCP response template in, and it will generate an
246 * MGCP_RecvFrom template that can match the primitives arriving on the
247 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800248 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
249 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100250 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200251 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800252 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200253 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800254 locPort := mp_local_udp_port,
255 msg := { response := resp }
256 }
257 return mrf;
258 }
259
260 /* Send a MGCP request + receive a (matching!) response */
261 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
262 var MgcpMessage msg := { command := valueof(cmd) };
263 resp.line.trans_id := cmd.line.trans_id;
264 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800265 var MGCP_RecvFrom mrf;
266 timer T := 5.0;
267
Harald Welte55015362017-11-18 16:02:42 +0100268 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800269 T.start;
270 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800271 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200272 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
273 setverdict(fail, "Response didn't match template");
274 mtc.stop;
275 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800276 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200277 [] T.timeout {
278 setverdict(fail, "Timeout waiting for response to ", cmd);
279 mtc.stop;
280 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800281 }
282 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800283
284 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
285 return mrf.msg.response;
286 } else {
287 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
288 return r;
289 }
Harald Welte00a067f2017-09-13 23:27:17 +0200290 }
291
Harald Welteba62c8c2017-11-18 18:26:49 +0100292 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
293 var integer i;
294 for (i := 0; i < lengthof(resp.params); i := i + 1) {
295 var MgcpParameter par := resp.params[i];
296 if (par.code == "I") {
297 return str2hex(par.val);
298 }
299 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200300 setverdict(fail, "Could not find conn id for MgcpReponse");
301 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100302 return '00000000'H;
303 }
304
Harald Welte10889c12017-11-18 19:40:31 +0100305 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
306 template MgcpCallId call_id := omit,
307 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100308 var template MgcpCommand cmd;
309 var MgcpResponse resp;
310 var template MgcpResponse rtmpl := {
311 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100312 code := ret_code,
313 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100314 },
315 params := *,
316 sdp := *
317 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100318 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100319 resp := mgcp_transceive_mgw(cmd, rtmpl);
320 }
321
Harald Welte10889c12017-11-18 19:40:31 +0100322 /* Send DLCX and expect OK response */
323 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
324 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100325 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100326 }
327
Harald Welteba62c8c2017-11-18 18:26:49 +0100328 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100329 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100330 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100331 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100332 }
333
Harald Weltebb7523b2018-03-29 08:52:01 +0200334 type record HostPort {
335 charstring hostname,
336 integer portnr optional
337 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200338 type record RtpOsmuxFlowData {
339 boolean local_cid_sent, /* whther non wildcarded CID was already sent to MGW */
340 MgcpOsmuxCID local_cid optional,
341 MgcpOsmuxCID remote_cid optional,
342 OsmuxemConfig cfg optional
343 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200344 type record RtpFlowData {
345 HostPort em, /* emulation side */
346 HostPort mgw, /* mgw side */
347 uint7_t pt,
348 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200349 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100350 RtpemConfig rtp_cfg optional,
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200351 RtpOsmuxFlowData osmux,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100352 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200353 }
354
Philipp Maier2321ef92018-06-27 17:52:04 +0200355 /* Create an RTP flow (bidirectional, or receive-only) */
356 function f_flow_create(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
Harald Weltebb7523b2018-03-29 08:52:01 +0200357 boolean one_phase := true)
358 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200359 var template MgcpCommand cmd;
360 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100361 var SDP_attribute_list attributes;
362
363 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
Philipp Maier465115e2023-02-07 11:17:24 +0100364 if (isvalue(flow.fmtp) and flow.fmtp != "") {
Philipp Maierc8c0b402019-03-07 10:48:45 +0100365 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
366 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200367
368 /* bind local RTP emulation socket */
369 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
370
Philipp Maier28bb8292018-07-20 17:09:17 +0200371 /* configure rtp-emulation */
372 if (ispresent(flow.rtp_cfg)) {
373 f_rtpem_configure(pt, flow.rtp_cfg);
374 } else {
375 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
376 rtp_cfg.tx_payload_type := flow.pt
377 f_rtpem_configure(pt, rtp_cfg);
378 }
379
Harald Weltebb7523b2018-03-29 08:52:01 +0200380 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200381 /* Connect flow to MGW using a CRCX that also contains an SDP
382 * part that tells the MGW where we are listening for RTP streams
383 * that come from the MGW. We get a fully working connection in
384 * one go. */
385
386 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200387 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100388 flow.em.portnr, { int2str(flow.pt) }, attributes);
389
Harald Weltebb7523b2018-03-29 08:52:01 +0200390 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
391 flow.mgcp_conn_id := extract_conn_id(resp);
392 /* extract port number from response */
393 flow.mgw.portnr :=
394 resp.sdp.media_list[0].media_field.ports.port_number;
395 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200396 /* Create a half-open connection only. We do not tell the MGW
397 * where it can send RTP streams to us. This means this
398 * connection will only be able to receive but can not send
399 * data back to us. In order to turn the connection in a fully
400 * bi-directional one, a separate MDCX is needed. */
401
402 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200403 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
404 flow.mgcp_conn_id := extract_conn_id(resp);
405 /* extract MGW-side port number from response */
406 flow.mgw.portnr :=
407 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200408 }
409 /* finally, connect the emulation-side RTP socket to the MGW */
410 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
411 }
412
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200413 /* Create an Osmux flow (bidirectional, or receive-only) */
414 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
415 boolean one_phase := true)
416 runs on dummy_CT {
417 var template MgcpCommand cmd;
418 var MgcpResponse resp;
419 var SDP_attribute_list attributes;
420 var OsmuxTxHandle tx_hdl;
421 var OsmuxRxHandle rx_hdl;
422 var charstring cid_response;
423 var OsmuxCID cid_resp_parsed
424
425 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
426 if (isvalue(flow.fmtp)) {
427 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
428 }
429
430 /* bind local Osmux emulation socket */
431 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
432
433 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200434 if (ispresent(flow.osmux.cfg)) {
435 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200436 } else {
437 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
438 f_osmuxem_configure(pt, osmux_cfg);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200439 flow.osmux.cfg := osmux_cfg
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200440 }
441
442 if (one_phase) {
443 /* Connect flow to MGW using a CRCX that also contains an SDP
444 * part that tells the MGW where we are listening for Osmux streams
445 * that come from the MGW. We get a fully working connection in
446 * one go. */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200447 if (flow.osmux.local_cid != -1) {
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200448 /* We may still want to negotiate osmux CID later at MDCX */
449 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200450 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200451 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200452 flow.osmux.local_cid_sent := true;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200453 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200454 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200455 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
456 flow.em.portnr, { int2str(flow.pt) }, attributes);
457 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
458 flow.mgcp_conn_id := extract_conn_id(resp);
459 /* extract port number from response */
460 flow.mgw.portnr :=
461 resp.sdp.media_list[0].media_field.ports.port_number;
462 } else {
463 /* Create a half-open connection only. We do not tell the MGW
464 * where it can send Osmux streams to us. This means this
465 * connection will only be able to receive but can not send
466 * data back to us. In order to turn the connection in a fully
467 * bi-directional one, a separate MDCX is needed. */
468
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200469 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200470 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
471
472 flow.mgcp_conn_id := extract_conn_id(resp);
473 /* extract MGW-side port number from response */
474 flow.mgw.portnr :=
475 resp.sdp.media_list[0].media_field.ports.port_number;
476 }
477
478 /* extract Osmux CID we got assigned by the MGW */
479 var MgcpMessage resp_msg := {
480 response := resp
481 }
482
483 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
484 setverdict(fail, "No Osmux CID in MGCP response", resp);
485 mtc.stop;
486 }
487
488 /* Make sure response is no wildcard */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200489 flow.osmux.remote_cid := f_mgcp_osmux_cid_decode(cid_response);
490 if (flow.osmux.remote_cid == -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200491 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
492 mtc.stop;
493 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200494 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux.remote_cid));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200495 f_osmuxem_register_txhandle(pt, tx_hdl);
496
497 /* finally, connect the emulation-side RTP socket to the MGW */
498 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
499 }
500
Philipp Maier2321ef92018-06-27 17:52:04 +0200501 /* Modify an existing RTP flow */
502 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
503 runs on dummy_CT {
504 var template MgcpCommand cmd;
505 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100506 var SDP_attribute_list attributes;
507
508 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
509 if (isvalue(flow.fmtp)) {
510 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
511 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200512
513 /* rebind local RTP emulation socket to the new address */
514 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
515
Philipp Maier28bb8292018-07-20 17:09:17 +0200516 /* reconfigure rtp-emulation */
517 if (ispresent(flow.rtp_cfg)) {
518 f_rtpem_configure(pt, flow.rtp_cfg);
519 } else {
520 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
521 rtp_cfg.tx_payload_type := flow.pt
522 f_rtpem_configure(pt, rtp_cfg);
523 }
524
Philipp Maier2321ef92018-06-27 17:52:04 +0200525 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
526 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
527 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100528 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200529 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
530
531 /* extract MGW-side port number from response. (usually this
532 * will not change, but thats is up to the MGW) */
533 flow.mgw.portnr :=
534 resp.sdp.media_list[0].media_field.ports.port_number;
535
536 /* reconnect the emulation-side RTP socket to the MGW */
537 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
538 }
539
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200540 /* Modify an existing Osmux flow */
541 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
542 runs on dummy_CT {
543 var template MgcpCommand cmd;
544 var MgcpResponse resp;
545 var SDP_attribute_list attributes;
546 var OsmuxRxHandle rx_hdl;
547 var charstring cid_response;
548 var OsmuxCID cid_resp_parsed
549
550 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
551 if (isvalue(flow.fmtp)) {
552 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
553 }
554
555 /* rebind local Osmux emulation socket to the new address */
556 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
557
558 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200559 if (ispresent(flow.osmux.cfg)) {
560 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200561 } else {
562 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
563 f_osmuxem_configure(pt, osmux_cfg);
564 }
565
566 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200567 if (flow.osmux.local_cid_sent == false and flow.osmux.local_cid != -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200568 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200569 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200570 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200571 flow.osmux.local_cid_sent := true;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200572 }
573
574 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200575 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux.local_cid);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200576 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
577 flow.em.portnr, { int2str(flow.pt) }, attributes);
578 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
579
580 /* extract MGW-side port number from response. (usually this
581 * will not change, but thats is up to the MGW) */
582 flow.mgw.portnr :=
583 resp.sdp.media_list[0].media_field.ports.port_number;
584
585 /* extract Osmux CID we got assigned by the MGW */
586 var MgcpMessage resp_msg := {
587 response := resp
588 }
589
590 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
591 setverdict(fail, "No Osmux CID in MGCP response", resp);
592 mtc.stop;
593 }
594
595 /* Make sure response is no wildcard */
596 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
597 if (cid_resp_parsed == -1) {
598 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
599 mtc.stop;
600 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200601 if (cid_resp_parsed != flow.osmux.remote_cid) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200602 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
603 mtc.stop;
604 }
605
606 /* reconnect the emulation-side Osmux socket to the MGW */
607 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
608 }
609
Philipp Maier2321ef92018-06-27 17:52:04 +0200610 /* Delete an existing RTP flow */
611 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
612 runs on dummy_CT {
613 var template MgcpCommand cmd;
614 var MgcpResponse resp;
615
616 /* Switch off RTP flow */
617 f_rtpem_mode(pt, RTPEM_MODE_NONE);
618
619 /* Delete connection on MGW (if needed) */
620 if (isvalue(call_id) and isvalue(ep)) {
621 f_sleep(0.1);
622 f_dlcx_ok(valueof(ep), call_id);
623 }
624 }
625
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200626 /* Delete an existing Osmux flow */
627 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
628 runs on dummy_CT {
629 var template MgcpCommand cmd;
630 var MgcpResponse resp;
631
632 /* Switch off Osmux flow */
633 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
634
635 /* Delete connection on MGW (if needed) */
636 if (isvalue(call_id) and isvalue(ep)) {
637 f_sleep(0.1);
638 f_dlcx_ok(valueof(ep), call_id);
639 }
640 }
641
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100642 function f_crcx(charstring ep_prefix) runs on dummy_CT {
643 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800644 var template MgcpCommand cmd;
645 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100646 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800647
Harald Welteedc45c12017-11-18 19:15:05 +0100648 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800649
Harald Welteba62c8c2017-11-18 18:26:49 +0100650 /* create the connection on the MGW */
651 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100652 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100653 extract_conn_id(resp);
654
655 /* clean-up */
656 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100657 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100658
Philipp Maier45635f42018-06-05 17:28:02 +0200659 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
660 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
661 var template MgcpCommand cmd;
662 var MgcpResponse resp;
663 var MgcpCallId call_id := '1234'H;
664
665 f_init(ep);
666
667 /* create the connection on the MGW */
668 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
669 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
670 extract_conn_id(resp);
671
672 /* clean-up */
673 f_dlcx_ok(ep, call_id);
674
675 /* See also OS#2658: Even when we omit the LCO information, we
676 expect the MGW to pick a sane payload type for us. This
677 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200678 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200679 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200680 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200681 }
682
683 /* See also OS#2658: We also expect the MGW to assign a port
684 number to us. */
685 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
686 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200687 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200688 }
689 }
690
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200691 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
692 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
693 var template MgcpCommand cmd;
694 var MgcpResponse resp;
695 var MgcpCallId call_id := '1234'H;
696 var charstring cid_response;
697
698 if (run_init) {
699 f_init(ep, true);
700 }
701
702 /* create the connection on the MGW */
703 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
704 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
705 extract_conn_id(resp);
706
707 /* extract Osmux CID we got assigned by the MGW */
708 var MgcpMessage resp_msg := {
709 response := resp
710 }
711
712 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
713 setverdict(fail, "No Osmux CID in MGCP response", resp);
714 mtc.stop;
715 }
716
717 /* Make sure response is no wildcard */
718 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
719 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
720 mtc.stop;
721 }
722
723 /* clean-up */
724 f_dlcx_ok(ep, call_id);
725 }
726
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100727 /* test valid CRCX without SDP */
728 testcase TC_crcx() runs on dummy_CT {
729 f_crcx(c_mgw_ep_rtpbridge);
730 setverdict(pass);
731 }
732
Philipp Maier45635f42018-06-05 17:28:02 +0200733 /* test valid CRCX without SDP and LCO */
734 testcase TC_crcx_no_lco() runs on dummy_CT {
735 f_crcx_no_lco(c_mgw_ep_rtpbridge);
736 setverdict(pass);
737 }
738
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100739 /* test valid CRCX without SDP (older method without endpoint prefix) */
740 testcase TC_crcx_noprefix() runs on dummy_CT {
741 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800742 setverdict(pass);
743 }
744
745 /* test CRCX with unsupported mode, expect 517 */
746 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
747 var template MgcpCommand cmd;
748 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100749 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100750 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800751 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
752
Harald Welteedc45c12017-11-18 19:15:05 +0100753 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800754
Harald Welteba62c8c2017-11-18 18:26:49 +0100755 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800756 resp := mgcp_transceive_mgw(cmd, rtmpl);
757 setverdict(pass);
758 }
759
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200760 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
761 testcase TC_crcx_osmo_ign() runs on dummy_CT {
762 var template MgcpCommand cmd;
763 var MgcpResponse resp;
764 var MgcpEndpoint ep := "7@" & c_mgw_domain;
765 var MgcpCallId call_id := '3'H;
766
767 f_init(ep);
768
769 /* CRCX 1 7@mgw MGCP 1.0
770 C: 3
771 L: p:20, a:GSM-EFR, nt:IN
772 M: recvonly
773 X-Osmo-IGN: C
774 */
775
776 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
777 cmd.params := {ts_MgcpParCallId(call_id),
778 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
779 t_MgcpParConnMode("recvonly"),
780 t_MgcpParOsmoIGN("C")};
781 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
782 extract_conn_id(resp);
783
784 /* clean-up */
785 f_dlcx_ok(ep, call_id);
786 setverdict(pass);
787 }
788
Harald Welte21ba5572017-09-19 17:55:05 +0800789 /* test CRCX with early bi-directional mode, expect 527 as
790 * bi-diretional media can only be established once both local and
791 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800792 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
793 var template MgcpCommand cmd;
794 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100795 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100796 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800797 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
798
Harald Welteedc45c12017-11-18 19:15:05 +0100799 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800800
Harald Welteba62c8c2017-11-18 18:26:49 +0100801 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800802 resp := mgcp_transceive_mgw(cmd, rtmpl);
803 setverdict(pass);
804 }
805
806 /* test CRCX with unsupported Parameters */
807 testcase TC_crcx_unsupp_param() runs on dummy_CT {
808 var template MgcpCommand cmd;
809 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100810 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100811 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800812 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
813
Harald Welteedc45c12017-11-18 19:15:05 +0100814 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800815
Harald Welteba62c8c2017-11-18 18:26:49 +0100816 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100817 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
818 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
819
Harald Weltee636afd2017-09-17 16:24:09 +0800820 resp := mgcp_transceive_mgw(cmd, rtmpl);
821 setverdict(pass);
822 }
823
824 /* test CRCX with missing CallId */
825 testcase TC_crcx_missing_callid() runs on dummy_CT {
826 var template MgcpCommand cmd;
827 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100828 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100829 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800830
Harald Welteedc45c12017-11-18 19:15:05 +0100831 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800832
Harald Welteba62c8c2017-11-18 18:26:49 +0100833 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800834 cmd.params := {
835 t_MgcpParConnMode("recvonly"),
836 t_MgcpParLocConnOpt("p:20")
837 }
838 resp := mgcp_transceive_mgw(cmd, rtmpl);
839 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100840
Harald Weltee636afd2017-09-17 16:24:09 +0800841 }
842
843 /* test CRCX with missing Mode */
844 testcase TC_crcx_missing_mode() runs on dummy_CT {
845 var template MgcpCommand cmd;
846 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100847 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100848 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100849 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800850
Harald Welteedc45c12017-11-18 19:15:05 +0100851 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800852
Harald Welteba62c8c2017-11-18 18:26:49 +0100853 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800854 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100855 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800856 t_MgcpParLocConnOpt("p:20")
857 }
858 resp := mgcp_transceive_mgw(cmd, rtmpl);
859 setverdict(pass);
860 }
861
862 /* test CRCX with unsupported packetization interval */
863 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
864 var template MgcpCommand cmd;
865 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100866 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100867 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100868 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800869
Harald Welteedc45c12017-11-18 19:15:05 +0100870 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800871
Harald Welteba62c8c2017-11-18 18:26:49 +0100872 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100873 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800874 resp := mgcp_transceive_mgw(cmd, rtmpl);
875 setverdict(pass);
876 }
877
878 /* test CRCX with illegal double presence of local connection option */
879 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
880 var template MgcpCommand cmd;
881 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100882 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100883 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800884 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
885
Harald Welteedc45c12017-11-18 19:15:05 +0100886 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800887
Harald Welteba62c8c2017-11-18 18:26:49 +0100888 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100889 /* p:20 is permitted only once and not twice! */
890 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800891 resp := mgcp_transceive_mgw(cmd, rtmpl);
892 setverdict(pass);
893 }
894
895 /* test valid CRCX with valid SDP */
896 testcase TC_crcx_sdp() runs on dummy_CT {
897 var template MgcpCommand cmd;
898 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100899 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100900 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800901
Harald Welteedc45c12017-11-18 19:15:05 +0100902 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800903
Harald Welteba62c8c2017-11-18 18:26:49 +0100904 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800905 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
906 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
907 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100908 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100909
910 /* clean-up */
911 f_dlcx_ok(ep, call_id);
912
Harald Weltee636afd2017-09-17 16:24:09 +0800913 setverdict(pass);
914 }
915
Philipp Maier5e06cee2018-02-01 18:28:08 +0100916 /* test valid wildcarded CRCX */
917 testcase TC_crcx_wildcarded() runs on dummy_CT {
918 var template MgcpCommand cmd;
919 var MgcpResponse resp;
920 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
921 var MgcpCallId call_id := '1234'H;
922 var MgcpEndpoint ep_assigned;
923 f_init();
924
925 /* create the connection on the MGW */
926 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
927 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
928 extract_conn_id(resp);
929
930 /* extract endpoint name we got assigned by the MGW */
931 var MgcpMessage resp_msg := {
932 response := resp
933 }
934 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
935 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200936 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100937 }
938
939 /* clean-up */
940 f_dlcx_ok(ep_assigned, call_id);
941
942 setverdict(pass);
943 }
944
945 /* test valid wildcarded CRCX */
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200946 type record of MgcpEndpoint MgcpEndpointList;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100947 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maier5e06cee2018-02-01 18:28:08 +0100948 var integer i;
949 var template MgcpCommand cmd;
950 var MgcpResponse resp;
951 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
952 var MgcpCallId call_id := '1234'H;
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200953 var MgcpEndpoint ep_assigned;
954 var MgcpEndpointList ep_assigned_li := {};
Philipp Maier5e06cee2018-02-01 18:28:08 +0100955 f_init();
956
957 /* Exhaust all endpoint resources on the virtual trunk */
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200958 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier5e06cee2018-02-01 18:28:08 +0100959 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
960 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
961
962 /* Make sure we got a connection id */
963 extract_conn_id(resp);
964
965 var MgcpMessage resp_msg := {
966 response := resp
967 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200968 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
Philipp Maier5e06cee2018-02-01 18:28:08 +0100969 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200970 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100971 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200972 ep_assigned_li := ep_assigned_li & {ep_assigned}
Philipp Maier5e06cee2018-02-01 18:28:08 +0100973 }
974
975 /* Try to allocate one more endpoint, which should fail */
976 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
977 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
978 resp := mgcp_transceive_mgw(cmd, rtmpl);
979 setverdict(pass);
980
981 /* clean-up */
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200982 for (i := 0; i < mp_num_endpoints; i := i+1) {
983 f_dlcx_ok(ep_assigned_li[i], call_id);
Philipp Maier5e06cee2018-02-01 18:28:08 +0100984 }
985 setverdict(pass);
986 }
987
Harald Weltee636afd2017-09-17 16:24:09 +0800988 /* TODO: various SDP related bits */
989
990
991 /* TODO: CRCX with X-Osmux */
992 /* TODO: double CRCX without force_realloc */
993
994 /* TODO: MDCX (various) */
995
996 /* TODO: MDCX without CRCX first */
997 testcase TC_mdcx_without_crcx() runs on dummy_CT {
998 var template MgcpCommand cmd;
999 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001000 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +01001001 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +08001002 var template MgcpResponse rtmpl := {
1003 line := {
1004 /* TODO: accept/enforce better error? */
1005 code := "400",
1006 string := ?
1007 },
1008 params:= { },
1009 sdp := omit
1010 };
1011
Harald Welteedc45c12017-11-18 19:15:05 +01001012 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001013
Harald Welte2bcfd3a2017-11-18 22:14:35 +01001014 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001015 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1016 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1017 valueof(ts_SDP_ptime(20)) });
1018 resp := mgcp_transceive_mgw(cmd, rtmpl);
1019 setverdict(pass);
1020 }
1021
1022 /* DLCX without CRCX first */
1023 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1024 var template MgcpCommand cmd;
1025 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001026 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001027 var template MgcpResponse rtmpl := {
1028 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001029 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001030 string := ?
1031 },
1032 params:= { },
1033 sdp := omit
1034 };
1035
Harald Welteedc45c12017-11-18 19:15:05 +01001036 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001037
Harald Welteedc45c12017-11-18 19:15:05 +01001038 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001039 resp := mgcp_transceive_mgw(cmd, rtmpl);
1040 setverdict(pass);
1041 }
1042
Philipp Maier21c1cff2021-07-20 14:22:53 +02001043 /* DLCX to non existing endpoint */
1044 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1045 var template MgcpCommand cmd;
1046 var MgcpResponse resp;
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001047 var charstring non_existant_ep := hex2str(int2hex(mp_num_endpoints + 1, 4))
1048 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & non_existant_ep & "@" & c_mgw_domain;
Philipp Maier21c1cff2021-07-20 14:22:53 +02001049 var template MgcpResponse rtmpl := {
1050 line := {
1051 code := ("500"),
1052 string := ?
1053 },
1054 params:= { },
1055 sdp := omit
1056 };
1057
1058 f_init(ep);
1059
1060 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1061 resp := mgcp_transceive_mgw(cmd, rtmpl);
1062 setverdict(pass);
1063 }
1064
Philipp Maier8a3dc922018-02-02 14:55:12 +01001065 /* test valid wildcarded MDCX */
1066 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1067 /* Note: A wildcarded MDCX is not allowed, so we expect the
1068 * MGW to reject this request */
1069 var template MgcpCommand cmd;
1070 var MgcpResponse resp;
1071 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1072 var MgcpCallId call_id := '1225'H;
1073 var template MgcpResponse rtmpl := {
1074 line := {
1075 /* TODO: accept/enforce better error? */
1076 code := "507",
1077 string := ?
1078 },
1079 params:= { },
1080 sdp := omit
1081 };
1082
1083 f_init(ep);
1084
1085 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1086 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1087 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1088 valueof(ts_SDP_ptime(20)) });
1089 resp := mgcp_transceive_mgw(cmd, rtmpl);
1090 setverdict(pass);
1091 }
1092
1093 /* test valid wildcarded DLCX */
1094 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001095 var template MgcpCommand cmd;
1096 var MgcpResponse resp;
1097 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001098 var integer i;
1099 var MgcpCallId call_id := '1234'H;
1100 var StatsDExpects expect;
1101 f_init(ep);
1102
1103 /* Allocate a few endpoints */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001104 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier55b90542021-07-02 12:33:19 +02001105 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1106 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1107 }
1108
Philipp Maier1298b092021-11-18 18:33:39 +01001109 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
1110 * occupied endpoints */
1111 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001112 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001113 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := mp_num_endpoints, max := mp_num_endpoints}
Philipp Maier55b90542021-07-02 12:33:19 +02001114 };
1115 f_statsd_expect(expect);
1116
1117 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001118 var template MgcpResponse rtmpl := {
1119 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001120 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001121 string := ?
1122 },
1123 params:= { },
1124 sdp := omit
1125 };
Philipp Maier55b90542021-07-02 12:33:19 +02001126 cmd := ts_DLCX(get_next_trans_id(), ep);
1127 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001128
Philipp Maier6c740e82022-06-30 12:04:34 +02001129 /* Query a the statsd once to ensure that intermediate results are pulled from the
1130 * pipeline. The second query (below) will return the actual result. */
1131 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001132 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := mp_num_endpoints}
Philipp Maier6c740e82022-06-30 12:04:34 +02001133 };
1134 f_statsd_expect(expect);
1135
1136 /* The second query must resturn a result with 0 endpoints in use. */
Philipp Maier55b90542021-07-02 12:33:19 +02001137 expect := {
1138 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1139 };
1140 f_statsd_expect(expect);
1141
Philipp Maier8a3dc922018-02-02 14:55:12 +01001142 setverdict(pass);
1143 }
1144
Harald Welte79181ff2017-11-18 19:26:11 +01001145 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1146 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1147 var template MgcpCommand cmd;
1148 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001149 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001150 var MgcpCallId call_id := '51234'H;
1151
1152 f_init(ep);
1153
1154 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1155 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1156
1157 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1158
1159 setverdict(pass);
1160 }
1161
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001162 /* test valid CRCX without SDP */
1163 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1164 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1165 setverdict(pass);
1166 }
1167
1168 /* test valid CRCX without SDP */
1169 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1170 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1171 setverdict(pass);
1172 }
1173
1174 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1175 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1176 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1177 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1178 setverdict(pass);
1179 }
1180
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02001181 /* test Creating 257 concurrent osmux conns. It should fail since maximum is 256. */
1182 testcase TC_crcx_osmux_257() runs on dummy_CT {
1183 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1184 var template MgcpCommand cmd;
1185 var MgcpResponse resp;
1186 var charstring cid_response;
1187 var integer i;
1188
1189 f_init(ep, true);
1190
1191 for (i := 0; i < 256; i := i + 1) {
1192
1193 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1194 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
1195 extract_conn_id(resp);
1196
1197 /* extract Osmux CID we got assigned by the MGW */
1198 var MgcpMessage resp_msg := {
1199 response := resp
1200 }
1201
1202 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
1203 setverdict(fail, "No Osmux CID in MGCP response", resp);
1204 mtc.stop;
1205 }
1206
1207 /* Make sure response is no wildcard */
1208 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
1209 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
1210 mtc.stop;
1211 }
1212 }
1213
1214 /* Now conn num 257, it should fail due to all Osmux conns already allocated: */
1215 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1216 resp := mgcp_transceive_mgw(cmd, tr_MgcpResp_Err("400"));
1217
1218 setverdict(pass);
1219
1220 /* Clean up */
1221 for (i := 0; i < 256; i := i + 1) {
1222 f_dlcx_ok(ep, int2hex(i, 4));
1223 }
1224 }
1225
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001226 /* Create one half open connection in receive-only mode. The MGW must accept
1227 * the packets but must not send any. */
1228 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1229 var RtpFlowData flow;
1230 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1231 var MgcpCallId call_id := '1225'H;
1232 var OsmuxemStats stats;
1233 var OsmuxTxHandle tx_hdl;
1234
1235 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001236 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001237 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001238 flow.osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001239 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1240
1241 /* create a transmitter not yet known by MGW */
1242 tx_hdl := valueof(t_TxHandleAMR590(2));
1243 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1244
1245 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1246 f_sleep(1.0);
1247 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1248
1249 stats := f_osmuxem_stats_get(OsmuxEM);
1250
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001251 if (stats.num_pkts_tx < 40 / flow.osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001252 setverdict(fail);
1253 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001254 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001255 setverdict(fail);
1256 }
1257
1258 f_osmuxem_stats_err_check(stats);
1259
1260 setverdict(pass);
1261 }
1262
1263 /* Create one connection in loopback mode, test if the Osmux packets are
1264 * actually reflected */
1265 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1266 var RtpFlowData flow;
1267 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1268 var MgcpCallId call_id := '1225'H;
1269 var OsmuxemStats stats;
1270 var OsmuxTxHandle tx_hdl;
1271
1272 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001273 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001274 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001275 flow.osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001276 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1277
1278 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1279 f_sleep(1.0);
1280
1281 /* Switch off both Tx, wait to receive delayed frames from MGW */
1282 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1283 f_sleep(0.1);
1284 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1285
1286 stats := f_osmuxem_stats_get(OsmuxEM);
1287
1288 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1289 setverdict(fail);
1290 }
1291 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1292 setverdict(fail);
1293 }
1294
1295 f_osmuxem_stats_err_check(stats);
1296
1297 setverdict(pass);
1298 }
1299
1300 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1301 * must match the reception statistics on the other side and vice versa. The
1302 * user may also supply a tolerance value (number of packets) when deviations
1303 * are acceptable */
1304 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1305 var integer plen;
1306
1307 log("stats A: ", a);
1308 log("stats B: ", b);
1309 log("tolerance: ", tolerance, " packets");
1310 log("batch_size: ", batch_size, " packets");
1311
1312 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1313
1314 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1315 return false;
1316 }
1317
1318 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1319 return false;
1320 }
1321
1322 if(a.num_pkts_tx > 0) {
1323 plen := a.bytes_payload_tx / a.num_pkts_tx;
1324 } else {
1325 plen := 0;
1326 }
1327
1328 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1329 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1330 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1331 return false;
1332 }
1333
1334 if (f_osmuxem_stats_compare_value(a.bytes_payload_rx, b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, tolerance_batch * plen) == false) {
1335 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1336 return false;
1337 }
1338
1339 return true;
1340 }
1341
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001342 function f_TC_two_crcx_and_rtp_osmux(boolean bidir, boolean rtp_amr_oa,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001343 charstring local_ip_rtp, charstring remote_ip_rtp,
1344 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001345 var RtpFlowData flow[2];
1346 var RtpemStats stats_rtp;
1347 var OsmuxemStats stats_osmux;
1348 var MgcpResponse resp;
1349 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1350 var MgcpCallId call_id := '1226'H;
1351 var integer tolerance := 0;
1352
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001353 var octetstring amr_payload;
1354 var charstring fmtp;
1355
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001356 f_init(ep, true);
1357
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001358 var AMRFT cmr := AMR_FT_0;
1359 var AMRFT ft := AMR_FT_2;
1360 if (rtp_amr_oa) {
1361 fmtp := "octet-align=1";
1362 var RTP_AMR_Hdr amr_oa_hdr := valueof(ts_RTP_AMR_Hdr(enum2int(cmr), enum2int(ft)));
1363 amr_payload := enc_RTP_AMR_Hdr(amr_oa_hdr) &
1364 f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload);
1365 } else {
1366 fmtp := "octet-align=0";
1367 /* Convert OA to BWE: */
1368 var RTP_AMR_BWE_Hdr amr_bwe_hdr := valueof(ts_RTP_AMR_BWE_Hdr(enum2int(cmr), enum2int(ft)));
1369 var bitstring amr_bwe_hdr_bits := substr(oct2bit(enc_RTP_AMR_BWE_Hdr(amr_bwe_hdr)), 0 , 10);
1370 var bitstring amr_data_bits := oct2bit(f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload));
1371 var bitstring amr_payload_bits := amr_bwe_hdr_bits & substr(amr_data_bits, 0, f_amrft_payload_bits_len(enum2int(ft)));
1372 amr_payload := bit2oct(f_pad_bit(amr_payload_bits, (lengthof(amr_payload_bits)+7)/8*8, '0'B));
1373 };
1374
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001375 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001376 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001377 flow[0].rtp_cfg := c_RtpemDefaultCfg
1378 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001379 flow[0].rtp_cfg.rx_fixed_payload := amr_payload;
1380 flow[0].rtp_cfg.tx_fixed_payload := amr_payload;
1381 flow[0].fmtp := fmtp;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001382 /* bind local RTP emulation sockets */
1383 flow[0].em.portnr := 10000;
1384 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1385
1386 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001387 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001388 flow[1].em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001389 flow[1].osmux.local_cid := 2;
1390 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001391 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1392
1393 if (bidir) {
1394 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1395 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1396
1397 /* Note: When we test bidirectional we may
1398 * loose packets during switch off because
1399 * both ends are transmitting and we only
1400 * can switch them off one by one. */
1401 tolerance := 3;
1402 } else {
1403 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1404 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1405 }
1406
1407 f_sleep(1.0);
1408
1409 /* Switch off both Tx, wait to receive delayed frames from MGW */
1410 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1411 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1412 f_sleep(0.1);
1413
1414 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1415 f_flow_delete(RTPEM[1]);
1416
1417 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1418 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001419 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux.cfg.batch_size, tolerance)) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001420 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1421 mtc.stop;
1422 }
1423
1424 f_rtpem_stats_err_check(stats_rtp);
1425 f_osmuxem_stats_err_check(stats_osmux);
1426
1427 setverdict(pass);
1428 }
1429
1430 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1431 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001432 f_TC_two_crcx_and_rtp_osmux(false, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001433 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001434 }
1435
1436 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1437 * exchange some data in both directions */
1438 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001439 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001440 mp_local_ipv4, mp_remote_ipv4);
1441 }
1442
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001443 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1444 * exchange some data in both directions. RTP side is configured to
1445 * rx/rx AMR in bandwidth-efficient mode. */
1446 testcase TC_two_crcx_and_rtp_osmux_bidir_amr_bwe() runs on dummy_CT {
1447 f_TC_two_crcx_and_rtp_osmux(true, false, mp_local_ipv4, mp_remote_ipv4,
1448 mp_local_ipv4, mp_remote_ipv4);
1449 }
1450
1451
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001452 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1453 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001454 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001455 mp_local_ipv6, mp_remote_ipv6);
1456 }
1457 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1458 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001459 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001460 mp_local_ipv6, mp_remote_ipv6);
1461 }
1462 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1463 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001464 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001465 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001466 }
1467
1468
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001469 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1470 charstring local_ip_rtp, charstring remote_ip_rtp,
1471 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001472 var RtpFlowData flow[2];
1473 var RtpemStats stats_rtp;
1474 var OsmuxemStats stats_osmux;
1475 var MgcpResponse resp;
1476 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1477 var MgcpCallId call_id := '1227'H;
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001478 var integer num_pkts_tx[2], num_pkts_rx[2];
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001479 var integer temp;
1480
1481 f_init(ep, true);
1482
1483 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001484 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001485 flow[0].rtp_cfg := c_RtpemDefaultCfg
1486 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1487 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1488 flow[0].rtp_cfg.rx_fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload);
1489 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1490 /* bind local RTP emulation sockets */
1491 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001492 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001493
1494
1495 /* Create the second connection. This connection will be also
1496 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001497 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001498 flow[1].em.portnr := mp_local_osmux_port;
1499 if (crcx_osmux_wildcard) {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001500 flow[1].osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001501 } else {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001502 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001503 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001504 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001505 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001506
1507
1508 /* The first leg starts transmitting */
1509 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1510 f_sleep(0.5);
1511 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1512 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1513 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1514 mtc.stop;
1515 }
1516 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1517 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1518 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1519 mtc.stop;
1520 }
1521
1522 /* The second leg starts transmitting a little later */
1523 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1524 f_sleep(1.0);
1525 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1526 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1527 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1528 mtc.stop;
1529 }
1530 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1531 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1532 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1533 mtc.stop;
1534 }
1535
1536 /* The first leg will now be switched into bidirectional
1537 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001538 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001539 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001540 /* At this point in time, flow[1](Osmux)->MGW->flow[0](RTP) is active,
1541 * hence if local CID was provided during CRCX we should already be seeing packets
1542 * flowing in one direction, aka stats_rtp.num_pkts_rx sould be >0 after a while: */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001543 f_sleep(0.5);
1544 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1545 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1546 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1547 mtc.stop;
1548 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001549 if (not crcx_osmux_wildcard and stats_rtp.num_pkts_rx == 0) {
1550 setverdict(fail, "received 0 packets Osmux->MGW->RTP");
1551 mtc.stop;
1552 }
1553
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001554 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1555 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1556 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1557 mtc.stop;
1558 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001559 if (stats_osmux.num_pkts_rx > 0) {
1560 setverdict(fail, "received unexpected ", stats_osmux.num_pkts_rx, " packets RTP->MGW->Osmux");
1561 mtc.stop;
1562 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001563
1564 /* When the second leg is switched into bidirectional mode
1565 * as well, then the MGW will connect the two together and
1566 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001567 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001568 if (crcx_osmux_wildcard) {
Pau Espin Pedrol982b9792022-10-07 11:48:13 +02001569 /* We set now the local CID in MDCX: */
1570 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001571 }
1572 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001573 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1574 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1575 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
1576 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
1577 num_pkts_rx[0] := stats_rtp.num_pkts_rx;
1578 num_pkts_rx[1] := stats_osmux.num_pkts_rx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001579 f_sleep(2.0);
1580
1581 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1582 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1583
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001584 temp := (stats_rtp.num_pkts_tx - num_pkts_tx[0]) -
1585 (stats_osmux.num_pkts_rx - num_pkts_rx[1]) * flow[1].osmux.cfg.batch_size;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001586 if (temp > 3 * flow[1].osmux.cfg.batch_size or temp < -3 * flow[1].osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001587 log("stats_rtp: ", stats_rtp);
1588 log("stats_osmux: ", stats_osmux);
1589 log("old_rtp_tx: ", num_pkts_tx[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001590 setverdict(fail, "RTP-Tx vs OSmux-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001591 mtc.stop;
1592 }
1593
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001594 temp := (stats_osmux.num_pkts_tx - num_pkts_tx[1]) -
1595 ((stats_rtp.num_pkts_rx - num_pkts_rx[0])/ flow[1].osmux.cfg.batch_size);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001596 if (temp > 3 or temp < -3) {
Pau Espin Pedroldb1e0682022-10-06 19:40:29 +02001597 log("stats_rtp: ", stats_rtp);
1598 log("stats_osmux: ", stats_osmux);
1599 log("old_osmux_tx: ", num_pkts_tx[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001600 setverdict(fail, "Osmux-Tx vs RTP-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001601 mtc.stop;
1602 }
1603
1604 f_rtpem_stats_err_check(stats_rtp);
1605 f_osmuxem_stats_err_check(stats_osmux);
1606
1607 /* Tear down */
1608 f_flow_delete(RTPEM[0]);
1609 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1610 setverdict(pass);
1611 }
1612
1613 /* create one RTP and one OSmux emulations and pass data in both
1614 directions. Create CRCX with wildcard Osmux CID and set it later
1615 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1616 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001617 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1618 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001619 }
1620
1621 /* create one RTP and one OSmux emulations and pass data in both
1622 directions. Create CRCX with fixed Osmux CID and keep it during
1623 MDCX. This is similar to how BSC sets up the call in AoIP. */
1624 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001625 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1626 mp_local_ipv4, mp_remote_ipv4);
1627 }
1628
1629 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1630 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1631 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1632 mp_local_ipv6, mp_remote_ipv6);
1633 }
1634 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1635 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1636 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1637 mp_local_ipv6, mp_remote_ipv6);
1638 }
1639 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1640 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1641 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1642 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001643 }
1644
Harald Welte646ecdb2017-12-28 03:21:57 +01001645 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1646 var template MgcpCommand cmd;
1647 var MgcpResponse resp;
1648
1649 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1650 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1651
1652 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1653
1654 setverdict(pass);
1655 }
1656
1657 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1658 var MgcpEndpoint ep;
1659 var MgcpCallId call_id;
1660 var integer ep_nr;
1661
1662 f_init();
1663
1664 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001665 if(ep_nr > 15) {
1666 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1667 } else {
1668 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1669 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001670 call_id := int2hex(ep_nr, 2) & '1234'H;
1671 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1672 }
1673 }
1674
Harald Welte79181ff2017-11-18 19:26:11 +01001675 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1676 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001677 var template MgcpCommand cmd;
1678 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001679 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001680 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001681
Harald Welteedc45c12017-11-18 19:15:05 +01001682 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001683
Harald Welteba62c8c2017-11-18 18:26:49 +01001684 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001685 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001686
Harald Welteba62c8c2017-11-18 18:26:49 +01001687 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001688
1689 setverdict(pass);
1690 }
1691
Harald Welte79181ff2017-11-18 19:26:11 +01001692 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1693 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1694 var template MgcpCommand cmd;
1695 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001696 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001697 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001698
1699 f_init(ep);
1700
1701 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1702 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1703
1704 f_dlcx_ok(ep);
1705
1706 setverdict(pass);
1707 }
1708
1709
Harald Welte6d167f82017-11-18 19:41:35 +01001710 /* CRCX + DLCX of valid endpoint but invalid call-id */
1711 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1712 var template MgcpCommand cmd;
1713 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001714 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001715 var MgcpCallId call_id := '51231'H;
1716
1717 f_init(ep);
1718
1719 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1720 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1721
1722 f_dlcx(ep, "516", *, 'ffff'H);
1723
1724 setverdict(pass);
1725 }
1726
1727
1728 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1729 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1730 var template MgcpCommand cmd;
1731 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001732 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001733 var MgcpCallId call_id := '51230'H;
1734
1735 f_init(ep);
1736
1737 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1738 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1739
1740 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1741
1742 setverdict(pass);
1743 }
1744
1745
Harald Weltee636afd2017-09-17 16:24:09 +08001746 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001747 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1748 var template MgcpCommand cmd;
1749 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001750 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001751 var MgcpCallId call_id := '51229'H;
1752 var template MgcpResponse rtmpl := {
1753 line := {
1754 code := "200",
1755 string := "OK"
1756 },
1757 params:= { },
1758 sdp := omit
1759 };
1760
1761 f_init(ep);
1762
1763 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1764 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1765
1766 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1767 resp := mgcp_transceive_mgw(cmd, rtmpl);
1768 resp := mgcp_transceive_mgw(cmd, rtmpl);
1769
1770 setverdict(pass);
1771 }
1772
Harald Weltebb7523b2018-03-29 08:52:01 +02001773 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1774 charstring codec) := {
1775 em := {
1776 hostname := host_a,
1777 portnr := omit
1778 },
1779 mgw := {
1780 hostname := host_b,
1781 portnr := omit
1782 },
1783 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001784 codec := codec,
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001785 osmux:= {
1786 local_cid_sent := false,
1787 local_cid := omit,
1788 remote_cid := omit,
1789 cfg := omit
1790 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001791 }
Harald Weltef53f1642017-11-18 19:57:11 +01001792
Harald Weltebb7523b2018-03-29 08:52:01 +02001793 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1794 testcase TC_rtpem_selftest() runs on dummy_CT {
1795 var RtpemStats stats[2];
1796 var integer local_port := 10000;
1797 var integer local_port2 := 20000;
1798
1799 f_init();
1800
1801 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1802 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1803
1804 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1805 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1806
1807 log("=== starting");
1808 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1809 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1810
1811 f_sleep(5.0);
1812
1813 log("=== stopping");
1814 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1815 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1816 f_sleep(0.5);
1817 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1818 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1819
1820 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1821 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1822 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1823 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001824 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001825 }
1826 setverdict(pass);
1827 }
1828
Philipp Maier2321ef92018-06-27 17:52:04 +02001829 /* Create one half open connection in receive-only mode. The MGW must accept
1830 * the packets but must not send any. */
1831 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1832 var RtpFlowData flow;
1833 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1834 var MgcpCallId call_id := '1225'H;
1835 var RtpemStats stats;
1836
1837 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001838 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001839 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001840 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001841
1842 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1843 f_sleep(1.0);
1844 f_flow_delete(RTPEM[0], ep, call_id);
1845
1846 stats := f_rtpem_stats_get(RTPEM[0]);
1847
Philipp Maier42b17cc2019-10-01 13:53:17 +02001848 /* Make sure that at least some amount of RTP packets/bytes
1849 * have has been transmitted. The compare values for
1850 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1851 * using a testrun and the results were devided by 2, so even
1852 * in load situations we should reach the minimum amount of
1853 * required packets/bytes */
1854
1855 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001856 setverdict(fail);
1857 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001858 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001859 setverdict(fail);
1860 }
Philipp Maier36291392018-07-25 09:40:44 +02001861
1862 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001863
1864 setverdict(pass);
1865 }
1866
1867 /* Create one connection in loopback mode, test if the RTP packets are
1868 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001869 function f_TC_one_crcx_loopback_rtp(charstring local_ip, charstring remote_ip, boolean one_phase := true) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001870 var RtpFlowData flow;
1871 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1872 var MgcpCallId call_id := '1225'H;
1873 var RtpemStats stats;
1874
1875 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001876 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001877 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001878 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001879
1880 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1881 f_sleep(1.0);
1882 f_flow_delete(RTPEM[0], ep, call_id);
1883
1884 stats := f_rtpem_stats_get(RTPEM[0]);
1885
1886 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1887 setverdict(fail);
1888 }
1889 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1890 setverdict(fail);
1891 }
Philipp Maier36291392018-07-25 09:40:44 +02001892
1893 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001894
1895 setverdict(pass);
1896 }
1897
Philipp Maier1ac13982021-05-07 23:06:07 +02001898 /* Create one connection in loopback mode, test if the RTP packets are
1899 * actually reflected */
1900 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001901 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001902 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001903 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1904 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1905 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001906
1907 /* Same as above, but we will intenionally not tell the MGW where to
1908 * send the outgoing traffic. The connection is still created in
1909 * loopback mode, so the MGW should take the originating address from
1910 * the incoming RTP packet and send it back to the source */
1911 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001912 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001913 }
1914
1915
Philipp Maier7df85f62018-07-25 10:26:09 +02001916 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1917 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001918 var RtpFlowData flow[2];
1919 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001920 var MgcpResponse resp;
1921 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1922 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001923 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001924
1925 f_init(ep);
1926
1927 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001928 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001929 /* bind local RTP emulation sockets */
1930 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001931 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001932
1933 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001934 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001935 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001936 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1937
1938 if (bidir) {
1939 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1940 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1941
1942 /* Note: When we test bidirectional we may
1943 * loose packets during switch off because
1944 * both ends are transmitting and we only
1945 * can switch them off one by one. */
1946 tolerance := 3;
1947 } else {
1948 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1949 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1950 }
1951
1952 f_sleep(1.0);
1953
1954 f_flow_delete(RTPEM[1]);
1955 f_flow_delete(RTPEM[0], ep, call_id);
1956
1957 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1958 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1959 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1960 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001961 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001962 }
1963
Philipp Maier36291392018-07-25 09:40:44 +02001964 f_rtpem_stats_err_check(stats[0]);
1965 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001966
Philipp Maier2321ef92018-06-27 17:52:04 +02001967 setverdict(pass);
1968 }
1969
1970 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1971 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001972 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001973 }
1974
1975 /* create two local RTP emulations; create two connections on MGW EP,
1976 * exchange some data in both directions */
1977 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001978 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1979 }
1980
1981 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1982 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1983 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1984 }
1985
1986 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1987 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1988 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001989 }
1990
1991 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001992 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01001993 charstring local_ip_b, charstring remote_ip_b,
1994 uint7_t payload_type := 3,
1995 charstring codec_name := "GSM/8000/1") runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001996 var RtpFlowData flow[2];
1997 var RtpemStats stats[2];
1998 var MgcpResponse resp;
1999 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2000 var MgcpCallId call_id := '1227'H;
2001 var integer num_pkts_tx[2];
2002 var integer temp;
2003
2004 f_init(ep);
2005
2006 /* Create the first connection in receive only mode */
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002007 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, payload_type, codec_name));
Philipp Maier2321ef92018-06-27 17:52:04 +02002008 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002009 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02002010
2011 /* Create the second connection. This connection will be also
2012 * in receive only mode */
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002013 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, payload_type, codec_name));
Philipp Maier2321ef92018-06-27 17:52:04 +02002014 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002015 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02002016
2017 /* The first leg starts transmitting */
2018 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2019 f_sleep(0.5);
2020 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2021 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002022 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002023 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002024 }
2025 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2026 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002027 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002028 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002029 }
2030
2031 /* The second leg starts transmitting a little later */
2032 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2033 f_sleep(1.0);
2034 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2035 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002036 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002037 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002038 }
2039 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2040 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002041 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002042 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002043 }
2044
2045 /* The first leg will now be switched into bidirectional
2046 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002047 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2048 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2049 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002050 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2051 f_sleep(0.5);
2052 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002053 if (stats[0].num_pkts_rx_err_disabled != 0) {
2054 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002055 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002056 }
2057 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2058 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002059 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002060 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002061 }
2062
2063 /* When the second leg is switched into bidirectional mode
2064 * as well, then the MGW will connect the two together and
2065 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02002066 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2067 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002068 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002069 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2070 f_sleep(2.0);
2071
2072 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2073 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2074
2075 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2076 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002077 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002078 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002079 }
2080
2081 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2082 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002083 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002084 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002085 }
Philipp Maier36291392018-07-25 09:40:44 +02002086
2087 f_rtpem_stats_err_check(stats[0]);
2088 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02002089
2090 /* Tear down */
2091 f_flow_delete(RTPEM[0]);
2092 f_flow_delete(RTPEM[1], ep, call_id);
2093 setverdict(pass);
2094 }
2095
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002096 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
2097 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2098 mp_local_ipv4, mp_remote_ipv4);
2099 }
2100
2101 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
2102 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
2103 mp_local_ipv6, mp_remote_ipv6);
2104 }
2105
2106 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
2107 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2108 mp_local_ipv6, mp_remote_ipv6);
2109 }
2110
Oliver Smith871f45a2023-01-24 16:21:50 +01002111 testcase TC_two_crcx_mdcx_and_rtp_clearmode() runs on dummy_CT {
2112 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2113 mp_local_ipv4, mp_remote_ipv4,
2114 120, /* 3GPP TS 48.103 table 5.4.2.2.1 */
2115 "CLEARMODE/8000");
2116 }
2117
Philipp Maier2321ef92018-06-27 17:52:04 +02002118 /* Test what happens when two RTP streams from different sources target
2119 * a single connection. Is the unsolicited stream properly ignored? */
2120 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2121 var RtpFlowData flow[2];
2122 var RtpemStats stats[2];
2123 var MgcpResponse resp;
2124 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2125 var MgcpCallId call_id := '1234321326'H;
2126 var integer unsolicited_port := 10002;
2127
2128 f_init(ep);
2129
2130 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002131 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002132 /* bind local RTP emulation sockets */
2133 flow[0].em.portnr := 10000;
2134 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2135
2136 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002137 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002138 flow[1].em.portnr := 20000;
2139 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002140
2141 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2142 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2143
Philipp Maier2321ef92018-06-27 17:52:04 +02002144 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002145
Philipp Maier2321ef92018-06-27 17:52:04 +02002146 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002147 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2148 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002149 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2150
2151 f_sleep(0.5);
2152
Daniel Willmanna069d382018-12-13 13:53:33 +01002153 /* Stop transmitting packets and tear down the flows */
2154 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002155 f_flow_delete(RTPEM[0]);
2156 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002157
2158 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2159 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2160 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2161 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002162 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002163 }
2164
Philipp Maier36291392018-07-25 09:40:44 +02002165 f_rtpem_stats_err_check(stats[0]);
2166 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002167
Harald Weltebb7523b2018-03-29 08:52:01 +02002168 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002169 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002170
Philipp Maier2321ef92018-06-27 17:52:04 +02002171 /* Test a handover situation. We first create two connections transmit
2172 * some data bidirectionally. Then we will simulate a handover situation. */
2173 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2174 var RtpFlowData flow[2];
2175 var RtpemStats stats[3];
2176 var MgcpResponse resp;
2177 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2178 var MgcpCallId call_id := '76338'H;
2179 var integer port_old;
2180
2181 f_init(ep);
2182
2183 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002184 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002185 /* bind local RTP emulation sockets */
2186 flow[0].em.portnr := 10000;
2187 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2188
2189 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002190 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002191 flow[1].em.portnr := 20000;
2192 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2193
2194 /* Normal rtp flow for one second */
2195 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2196 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2197 f_sleep(1.0);
2198
2199 /* Now switch the flow over to a new port (BTS) */
2200 port_old := flow[0].em.portnr;
2201 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002202 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002203 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002204 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002205
2206 /* When handing over a call, the old source may still keep
2207 * transmitting for a while. We simulate this by injecting
2208 * some unsolicited packets on the behalf of the old source,
2209 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002210 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2211 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002212 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2213 f_sleep(1.0);
2214 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2215 f_sleep(1.0);
2216
2217 /* Terminate call */
2218 f_flow_delete(RTPEM[0]);
2219 f_flow_delete(RTPEM[1], ep, call_id);
2220
2221 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2222 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2223 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2224 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002225 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002226 }
2227 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2228 if (stats[2].num_pkts_rx_err_disabled != 0) {
2229 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002230 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002231 }
2232
Philipp Maier36291392018-07-25 09:40:44 +02002233 f_rtpem_stats_err_check(stats[0]);
2234 f_rtpem_stats_err_check(stats[1]);
2235 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002236
Philipp Maier2321ef92018-06-27 17:52:04 +02002237 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002238 }
Harald Weltef53f1642017-11-18 19:57:11 +01002239
Philipp Maier6d4e0942019-02-21 17:35:01 +01002240
2241 /* create two local RTP emulations; create two connections on MGW EP, see if
Philipp Maier8ed48c52023-02-07 11:24:31 +01002242 * exchanged data is converted between ts101318 and rfc5993 */
2243 function f_ts101318_rfc5993_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
Philipp Maier6d4e0942019-02-21 17:35:01 +01002244 var RtpFlowData flow[2];
2245 var RtpemStats stats[2];
2246 var MgcpResponse resp;
2247 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2248 var MgcpCallId call_id := '1226'H;
2249
2250 f_init(ep);
2251
2252 /* Turn on conversion mode */
2253 f_vty_enter_config(MGWVTY);
2254 f_vty_transceive(MGWVTY, "mgcp");
2255 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2256
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002257 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002258 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002259 /* bind local RTP emulation sockets */
2260 flow[0].em.portnr := 10000;
2261 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2262 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
Philipp Maier8ed48c52023-02-07 11:24:31 +01002263 flow[0].rtp_cfg.rx_fixed_payload := pl0
2264 flow[0].rtp_cfg.tx_fixed_payload := pl0
2265 flow[0].fmtp := fmtp0;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002266 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2267
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002268 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002269 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002270 flow[1].em.portnr := 20000;
2271 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2272 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
Philipp Maier8ed48c52023-02-07 11:24:31 +01002273 flow[1].rtp_cfg.rx_fixed_payload := pl1
2274 flow[1].rtp_cfg.tx_fixed_payload := pl1
2275 flow[1].fmtp := fmtp1;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002276 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2277
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002278 /* Send RTP packets to connection #0, receive on connection #1 */
2279 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2280 f_sleep(0.5);
2281 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002282 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002283 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2284 f_sleep(0.5);
2285 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002286
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002287 /* Send RTP packets to connection #1, receive on connection #0 */
2288 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2289 f_sleep(0.5);
2290 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2291 f_sleep(1.0);
2292 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2293 f_sleep(0.5);
2294 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2295
2296 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002297 f_flow_delete(RTPEM[0]);
2298 f_flow_delete(RTPEM[1], ep, call_id);
2299
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002300 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002301 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2302 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002303 f_rtpem_stats_err_check(stats[0]);
2304 f_rtpem_stats_err_check(stats[1]);
2305
2306 /* Turn off conversion mode */
2307 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2308
2309 setverdict(pass);
2310 }
2311
Philipp Maier8ed48c52023-02-07 11:24:31 +01002312 const octetstring rtp_hr_gsm_ts101318 := '0b11b3eede60be4e3ec68838c7b5'O;
2313 const octetstring rtp_hr_gsm_rfc5993 := '000b11b3eede60be4e3ec68838c7b5'O;
2314
2315 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2316 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "", "");
2317 }
2318
2319 testcase TC_ts101318_rfc5993_rtp_conversion_fmtp() runs on dummy_CT {
2320 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "gsm-hr-format=ts101318", "gsm-hr-format=rfc5993");
2321 }
2322
Philipp Maier4f764ce2019-03-07 10:54:10 +01002323 /* create two local RTP emulations; create two connections on MGW EP, see if
2324 * exchanged data is converted between AMR octet-aligned and bandwith
2325 * efficient-mode */
2326 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2327 var RtpFlowData flow[2];
2328 var RtpemStats stats[2];
2329 var MgcpResponse resp;
2330 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2331 var MgcpCallId call_id := '1226'H;
2332
2333 f_init(ep);
2334
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002335 /* Connection #0 (Bidirectional) */
Philipp Maiere5af8a32022-03-03 11:16:02 +01002336 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 96, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002337 /* bind local RTP emulation sockets */
2338 flow[0].em.portnr := 10000;
2339 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2340 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2341 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2342 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2343 flow[0].fmtp := fmtp0;
2344 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2345
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002346 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002347 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002348 flow[1].em.portnr := 20000;
2349 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2350 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2351 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2352 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2353 flow[1].fmtp := fmtp1;
2354 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2355
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002356 /* Send RTP packets to connection #0, receive on connection #1 */
2357 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2358 f_sleep(0.5);
2359 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002360 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002361 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2362 f_sleep(0.5);
2363 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002364
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002365 /* Send RTP packets to connection #1, receive on connection #0 */
2366 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2367 f_sleep(0.5);
2368 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2369 f_sleep(1.0);
2370 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2371 f_sleep(0.5);
2372 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2373
2374 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002375 f_flow_delete(RTPEM[0]);
2376 f_flow_delete(RTPEM[1], ep, call_id);
2377
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002378 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002379 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2380 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002381 f_rtpem_stats_err_check(stats[0]);
2382 f_rtpem_stats_err_check(stats[1]);
2383
2384 setverdict(pass);
2385 }
2386
Philipp Maier882843d2020-05-25 15:33:13 +02002387 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2388 * functions are real world AMR RTP payloads including AMR header. The
2389 * payloads were extracted from a trace with known good payloads. */
2390
Philipp Maier4f764ce2019-03-07 10:54:10 +01002391 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002392 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002393 }
2394
2395 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2396 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2397 }
2398
2399 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2400 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2401 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002402
Harald Weltee636afd2017-09-17 16:24:09 +08002403 /* TODO: Double-DLCX (no retransmission) */
2404
2405
2406
2407 /* TODO: AUEP (various) */
2408 /* TODO: RSIP (various) */
2409 /* TODO: RQNT (various) */
2410 /* TODO: EPCF (various) */
2411 /* TODO: AUCX (various) */
2412 /* TODO: invalid verb (various) */
2413
Oliver Smith021141e2019-06-25 12:09:01 +02002414
2415 testcase TC_conn_timeout() runs on dummy_CT {
2416 var RtpFlowData flow;
2417 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2418 var MgcpCallId call_id := '1225'H;
2419 var MGCP_RecvFrom mrf;
2420
2421 f_init(ep);
2422 log("Setting conn-timeout to 1s");
2423 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2424
2425 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002426 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002427 flow.em.portnr := 10000;
2428 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2429 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2430 f_sleep(1.5);
2431
2432 log("Stopping for 0.5s and resuming");
2433 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2434 f_sleep(0.5);
2435 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2436 f_sleep(0.1);
2437
2438 log("Stopping for 1.5s, expecting to run into timeout");
2439 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2440 f_sleep(1.5);
2441
2442 log("Resuming should fail now");
2443 f_rtpem_conn_refuse_expect(RTPEM[0]);
2444 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2445 f_sleep(0.2);
2446 f_rtpem_conn_refuse_verify(RTPEM[0]);
2447
2448 setverdict(pass);
2449 }
2450
Philipp Maier2609c752020-07-08 12:38:09 +02002451 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2452 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2453 var template MgcpCommand cmd;
2454 var MgcpResponse resp;
2455 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2456 var MgcpCallId call_id := '8376F297'H;
2457
2458 f_init(ep);
2459
2460 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2461 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2462
2463 f_dlcx_ok(ep);
2464
2465 setverdict(pass);
2466 }
2467
2468 /* Test what happens when overlapping endpoints are selected (E1) */
2469 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2470 var template MgcpCommand cmd;
2471 var MgcpResponse resp;
2472 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2473 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2474 var MgcpCallId call_id_1 := '8376F297'H;
2475 var MgcpCallId call_id_2 := '837AF2A7'H;
2476
2477 f_init();
2478
2479 /* ep_1 and ep_2 are overlapping, selecting both one after
2480 * another should work fine: */
2481 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2482 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2483 f_dlcx_ok(ep_1);
2484 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2485 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2486 f_dlcx_ok(ep_2);
2487
2488 /* When ep_1 is serving a call we can not select ep_2 becaus
2489 * it is overlapping with ep_1 */
2490 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2491 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2492 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2493 resp := mgcp_transceive_mgw(cmd, ?);
2494 if (resp.line.code != "501") {
2495 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2496 }
2497 f_dlcx_ok(ep_1);
2498
2499 setverdict(pass);
2500 }
2501
2502 /* Create one connection in loopback mode, test if the RTP packets are
2503 * actually reflected */
2504 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2505 var RtpFlowData flow;
2506 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2507 var MgcpCallId call_id := '12250989'H;
2508 var RtpemStats stats;
2509
2510 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002511 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002512 flow.em.portnr := 10000;
2513 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2514
2515 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2516 f_sleep(1.0);
2517 f_flow_delete(RTPEM[0], ep, call_id);
2518
2519 stats := f_rtpem_stats_get(RTPEM[0]);
2520
2521 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2522 setverdict(fail);
2523 }
2524 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2525 setverdict(fail);
2526 }
2527
2528 f_rtpem_stats_err_check(stats);
2529
2530 setverdict(pass);
2531 }
2532
Philipp Maier13aff992022-06-30 16:19:59 +02002533 /* test valid wildcarded DLCX on an E1 trunk */
2534 testcase TC_e1_dlcx_wildcarded() runs on dummy_CT {
2535 var template MgcpCommand cmd;
2536 var MgcpEndpoint ep;
2537 var MgcpCallId call_id := '8376F297'H;
2538 var integer n_e1_ts := 4;
2539 var StatsDExpects expect;
2540
2541 f_init();
2542
2543 /* Open a few E1 timeslots */
2544 for (var integer i := 0; i < n_e1_ts; i := i+1) {
2545 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-0@" & c_mgw_domain;
2546 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2547 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2548 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-2@" & c_mgw_domain;
2549 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2550 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2551 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-4@" & c_mgw_domain;
2552 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2553 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2554 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-6@" & c_mgw_domain;
2555 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2556 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2557 }
2558
2559 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
2560 * occupied endpoints */
2561 f_sleep(1.0)
2562 expect := {
2563 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := n_e1_ts * 4, max := n_e1_ts * 4}
2564 };
2565 f_statsd_expect(expect);
2566
2567 /* Send wildcarded DLCX */
2568 var template MgcpResponse rtmpl := {
2569 line := {
2570 code := "200",
2571 string := ?
2572 },
2573 params:= { },
2574 sdp := omit
2575 };
2576 ep := "ds/e1-1/*@" & c_mgw_domain;
2577 cmd := ts_DLCX(get_next_trans_id(), ep);
2578 mgcp_transceive_mgw(cmd, rtmpl);
2579
2580 /* Query a the statsd once to ensure that intermediate results are pulled from the
2581 * pipeline. The second query (below) will return the actual result. */
2582 expect := {
2583 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := n_e1_ts * 4}
2584 };
2585 f_statsd_expect(expect);
2586
2587 /* The second query must resturn a result with 0 endpoints in use. */
2588 expect := {
2589 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := 0}
2590 };
2591 f_statsd_expect(expect);
2592
2593 setverdict(pass);
2594 }
2595
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002596 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2597 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2598 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2599 var template MgcpCommand cmd;
2600 var MgcpResponse resp;
2601 var MgcpCallId call_id := '1234'H;
2602 var MgcpConnectionId conn_id;
2603
2604 f_init(ep);
2605
2606 /* create the connection on the MGW */
2607 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2608 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2609 conn_id := extract_conn_id(resp);
2610
2611 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2612 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2613 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2614 valueof(ts_SDP_ptime(20)) });
2615 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2616
2617 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2618 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2619 }
2620 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2621 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2622 }
2623
2624 /* clean-up */
2625 f_dlcx_ok(ep, call_id);
2626 setverdict(pass);
2627 }
2628
2629 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2630 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2631 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2632 var template MgcpCommand cmd;
2633 var MgcpResponse resp;
2634 var MgcpCallId call_id := '1234'H;
2635 var MgcpConnectionId conn_id;
2636
2637 f_init(ep);
2638
2639 /* create the connection on the MGW */
2640 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2641 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2642 conn_id := extract_conn_id(resp);
2643
2644 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2645 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2646 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2647 valueof(ts_SDP_ptime(20)) });
2648 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2649
2650 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2651 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2652 }
2653 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2654 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2655 }
2656
2657 /* clean-up */
2658 f_dlcx_ok(ep, call_id);
2659 setverdict(pass);
2660 }
2661
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002662 /* create two local RTP+IuUP emulations and pass data in both directions */
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002663 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002664 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002665 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2666 var RtpFlowData flow[2];
2667 var RtpemStats stats[2];
2668 var MgcpResponse resp;
2669 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2670 var MgcpCallId call_id := '1227'H;
2671 var integer num_pkts_tx[2];
2672 var integer temp;
2673
2674 f_init(ep);
2675
2676 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2677 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2678 flow[0].em.portnr := 10000;
2679 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2680 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2681 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002682 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002683 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002684 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2685 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2686
2687 /* Create the second connection. This connection will be also
2688 * in receive only mode (CN side, IuUP-Init passive) */
2689 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2690 flow[1].em.portnr := 20000;
2691 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2692 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2693 flow[1].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002694 flow[1].rtp_cfg.iuup_cfg.active_init := false;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002695 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2696 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2697
2698 /* The first leg starts transmitting */
2699 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2700 f_sleep(0.5);
2701 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2702 if (stats[0].num_pkts_rx_err_disabled != 0) {
2703 setverdict(fail, "received packets from MGW on recvonly connection 0");
2704 mtc.stop;
2705 }
2706 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2707 if (stats[1].num_pkts_rx_err_disabled != 0) {
2708 setverdict(fail, "received packets from MGW on recvonly connection 1");
2709 mtc.stop;
2710 }
2711
2712 /* The second leg starts transmitting a little later */
2713 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2714 f_sleep(1.0);
2715 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2716 if (stats[0].num_pkts_rx_err_disabled != 0) {
2717 setverdict(fail, "received packets from MGW on recvonly connection 0");
2718 mtc.stop;
2719 }
2720 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2721 if (stats[1].num_pkts_rx_err_disabled != 0) {
2722 setverdict(fail, "received packets from MGW on recvonly connection 1");
2723 mtc.stop;
2724 }
2725
2726 /* The first leg will now be switched into bidirectional
2727 * mode, but we do not expect any data coming back yet. */
2728 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2729 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2730 num_pkts_tx[1] := stats[1].num_pkts_tx;
2731 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2732 f_sleep(0.5);
2733 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2734 if (stats[0].num_pkts_rx_err_disabled != 0) {
2735 setverdict(fail, "received packets from MGW on recvonly connection 0");
2736 mtc.stop;
2737 }
2738 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2739 if (stats[1].num_pkts_rx_err_disabled != 0) {
2740 setverdict(fail, "received packets from MGW on recvonly connection 1");
2741 mtc.stop;
2742 }
2743
2744 /* When the second leg is switched into bidirectional mode
2745 * as well, then the MGW will connect the two together and
2746 * we should see RTP streams passing through from both ends. */
2747 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2748 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2749 num_pkts_tx[0] := stats[0].num_pkts_tx;
2750 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2751 f_sleep(2.0);
2752
2753 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2754 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2755
2756 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2757 if (temp > 3 or temp < -3) {
2758 setverdict(fail, "number of packets not within normal parameters:", temp);
2759 mtc.stop;
2760 }
2761
2762 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2763 if (temp > 3 or temp < -3) {
2764 setverdict(fail, "number of packets not within normal parameters:", temp);
2765 mtc.stop;
2766 }
2767
2768 f_rtpem_stats_err_check(stats[0]);
2769 f_rtpem_stats_err_check(stats[1]);
2770
2771 /* Tear down */
2772 f_flow_delete(RTPEM[0]);
2773 f_flow_delete(RTPEM[1], ep, call_id);
2774 setverdict(pass);
2775 }
2776 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002777 var template (value) IuUP_RabFlowCombinationList rfcl := {
2778 t_IuUP_RFC_AMR_12_2(0),
2779 t_IuUP_RFC_AMR_SID(1),
2780 t_IuUP_RFC_AMR_NO_DATA(2)
2781 };
2782 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2783 mp_local_ipv4, mp_remote_ipv4);
2784 }
2785 /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */
2786 testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT {
2787 var template (value) IuUP_RabFlowCombinationList rfcl := {
2788 t_IuUP_RFC_AMR_12_2(1),
2789 t_IuUP_RFC_AMR_SID(2),
2790 t_IuUP_RFC_AMR_NO_DATA(0)
2791 };
2792 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002793 mp_local_ipv4, mp_remote_ipv4);
2794 }
2795
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002796 /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */
2797 function f_tc_two_crcx_mdcx_and_iuup_rtp(charstring local_ip_a, charstring remote_ip_a,
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002798 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002799 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2800 var RtpFlowData flow[2];
2801 var RtpemStats stats[2];
2802 var MgcpResponse resp;
2803 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2804 var MgcpCallId call_id := '1227'H;
2805 var integer num_pkts_tx[2];
2806 var integer temp;
2807
2808 f_init(ep);
2809
2810 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2811 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2812 flow[0].em.portnr := 10000;
2813 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2814 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2815 flow[0].rtp_cfg.tx_fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
2816 flow[0].rtp_cfg.rx_fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O; /* flow[1].rtp_cfg.tx_fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
2817 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002818 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002819 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002820 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2821 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2822
2823 /* Create the second connection. This connection will be also
2824 * in receive only mode (CN side, regular RTP) */
2825 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
2826 flow[1].em.portnr := 20000;
2827 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2828 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2829 flow[1].rtp_cfg.tx_fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
2830 flow[1].rtp_cfg.rx_fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O; /* flow[0].rtp_cfg.tx_fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
2831 flow[1].rtp_cfg.iuup_mode := false;
2832 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2833 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2834
2835 /* The first leg starts transmitting */
2836 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2837 f_sleep(0.5);
2838 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2839 if (stats[0].num_pkts_rx_err_disabled != 0) {
2840 setverdict(fail, "received packets from MGW on recvonly connection 0");
2841 mtc.stop;
2842 }
2843 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2844 if (stats[1].num_pkts_rx_err_disabled != 0) {
2845 setverdict(fail, "received packets from MGW on recvonly connection 1");
2846 mtc.stop;
2847 }
2848
2849 /* The second leg starts transmitting a little later */
2850 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2851 f_sleep(1.0);
2852 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2853 if (stats[0].num_pkts_rx_err_disabled != 0) {
2854 setverdict(fail, "received packets from MGW on recvonly connection 0");
2855 mtc.stop;
2856 }
2857 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2858 if (stats[1].num_pkts_rx_err_disabled != 0) {
2859 setverdict(fail, "received packets from MGW on recvonly connection 1");
2860 mtc.stop;
2861 }
2862
2863 /* The first leg will now be switched into bidirectional
2864 * mode, but we do not expect any data coming back yet. */
2865 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2866 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2867 num_pkts_tx[1] := stats[1].num_pkts_tx;
2868 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2869 f_sleep(0.5);
2870 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2871 if (stats[0].num_pkts_rx_err_disabled != 0) {
2872 setverdict(fail, "received packets from MGW on recvonly connection 0");
2873 mtc.stop;
2874 }
2875 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2876 if (stats[1].num_pkts_rx_err_disabled != 0) {
2877 setverdict(fail, "received packets from MGW on recvonly connection 1");
2878 mtc.stop;
2879 }
2880
2881 /* When the second leg is switched into bidirectional mode
2882 * as well, then the MGW will connect the two together and
2883 * we should see RTP streams passing through from both ends. */
2884 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2885 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2886 num_pkts_tx[0] := stats[0].num_pkts_tx;
2887 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2888 f_sleep(2.0);
2889
2890 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2891 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2892
2893 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2894 if (temp > 3 or temp < -3) {
2895 setverdict(fail, "number of packets not within normal parameters:", temp);
2896 mtc.stop;
2897 }
2898
2899 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2900 if (temp > 3 or temp < -3) {
2901 setverdict(fail, "number of packets not within normal parameters:", temp);
2902 mtc.stop;
2903 }
2904
2905 f_rtpem_stats_err_check(stats[0]);
2906 f_rtpem_stats_err_check(stats[1]);
2907
2908 /* Tear down */
2909 f_flow_delete(RTPEM[0]);
2910 f_flow_delete(RTPEM[1], ep, call_id);
2911 setverdict(pass);
2912 }
2913 testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002914 var template (value) IuUP_RabFlowCombinationList rfcl := {
2915 t_IuUP_RFC_AMR_12_2(0),
2916 t_IuUP_RFC_AMR_SID(1),
2917 t_IuUP_RFC_AMR_NO_DATA(2)
2918 };
2919 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2920 mp_local_ipv4, mp_remote_ipv4);
2921 }
2922 /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */
2923 testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT {
2924 var template (value) IuUP_RabFlowCombinationList rfcl := {
2925 t_IuUP_RFC_AMR_12_2(1),
2926 t_IuUP_RFC_AMR_SID(2),
2927 t_IuUP_RFC_AMR_NO_DATA(0)
2928 };
2929 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002930 mp_local_ipv4, mp_remote_ipv4);
2931 }
2932
Harald Welte00a067f2017-09-13 23:27:17 +02002933 control {
2934 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002935 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002936 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002937 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002938 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002939 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002940 execute(TC_crcx_early_bidir_mode());
2941 execute(TC_crcx_unsupp_param());
2942 execute(TC_crcx_missing_callid());
2943 execute(TC_crcx_missing_mode());
2944 execute(TC_crcx_unsupp_packet_intv());
2945 execute(TC_crcx_illegal_double_lco());
2946 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002947 execute(TC_crcx_wildcarded());
2948 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002949 execute(TC_mdcx_without_crcx());
2950 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02002951 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002952 execute(TC_mdcx_wildcarded());
2953 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002954 execute(TC_crcx_and_dlcx_ep_callid_connid());
2955 execute(TC_crcx_and_dlcx_ep_callid());
2956 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002957 execute(TC_crcx_and_dlcx_ep_callid_inval());
2958 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002959 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002960
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002961 execute(TC_crcx_osmux_wildcard());
2962 execute(TC_crcx_osmux_fixed());
2963 execute(TC_crcx_osmux_fixed_twice());
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02002964 execute(TC_crcx_osmux_257());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002965 execute(TC_one_crcx_receive_only_osmux());
2966 execute(TC_one_crcx_loopback_osmux());
2967 execute(TC_two_crcx_and_rtp_osmux());
2968 execute(TC_two_crcx_and_rtp_osmux_bidir());
Pau Espin Pedrol26258472022-10-25 12:51:24 +02002969 execute(TC_two_crcx_and_rtp_osmux_bidir_amr_bwe());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002970 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2971 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2972
Harald Welte33d82162017-12-28 03:21:57 +01002973 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002974
2975 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002976
2977 execute(TC_one_crcx_receive_only_rtp());
2978 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002979 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02002980 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002981 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002982 execute(TC_two_crcx_diff_pt_and_rtp());
2983 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002984 execute(TC_two_crcx_mdcx_and_rtp());
2985 execute(TC_two_crcx_and_unsolicited_rtp());
2986 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002987 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier8ed48c52023-02-07 11:24:31 +01002988 execute(TC_ts101318_rfc5993_rtp_conversion_fmtp());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002989 execute(TC_amr_oa_bwe_rtp_conversion());
2990 execute(TC_amr_oa_oa_rtp_conversion());
2991 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002992
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002993 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002994
2995 execute(TC_e1_crcx_and_dlcx_ep());
2996 execute(TC_e1_crcx_with_overlap());
2997 execute(TC_e1_crcx_loopback());
Philipp Maier13aff992022-06-30 16:19:59 +02002998 execute(TC_e1_dlcx_wildcarded());
Philipp Maier2609c752020-07-08 12:38:09 +02002999
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02003000 execute(TC_crcx_mdcx_ip4());
3001 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02003002 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
3003 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
3004 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
3005 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
3006 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
3007 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
3008 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
3009 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02003010
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003011 execute(TC_two_crcx_mdcx_and_iuup());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003012 execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered());
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003013 execute(TC_two_crcx_mdcx_and_iuup_rtp());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003014 execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered());
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003015
Oliver Smith871f45a2023-01-24 16:21:50 +01003016 execute(TC_two_crcx_mdcx_and_rtp_clearmode());
3017
Philipp Maier37965082021-05-25 16:44:25 +02003018 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
3019 * older versions of osmo-mgw. This eventually leads into
3020 * a failure of all subsequent testcases, so it is important
3021 * not to add new testcaes after this one. */
3022 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02003023 }
3024}