blob: 7568ac9f73e7b6a6b611756bc07f508b624feaf9 [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 {
Pau Espin Pedrole6d095c2023-09-27 14:47:45 +020013 import from Misc_Helpers all;
Harald Weltef07c2862017-11-18 17:16:24 +010014 import from Osmocom_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +020015 import from MGCP_Types all;
Harald Welte4029e8c2017-11-23 22:00:42 +010016 import from MGCP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080017 import from SDP_Types all;
18 import from MGCP_CodecPort all;
19 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +010020 import from RTP_CodecPort all;
21 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020022 import from RTP_Emulation all;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +020023 import from IuUP_Emulation all;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020024 import from OSMUX_Types all;
25 import from OSMUX_CodecPort all;
26 import from OSMUX_CodecPort_CtrlFunct all;
27 import from OSMUX_Emulation all;
Pau Espin Pedrol71ed4632022-09-02 18:51:19 +020028 import from AMR_Types all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080029 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010030 import from General_Types all;
31 import from Native_Functions all;
32 import from IPCP_Types all;
33 import from IP_Types all;
34 import from Osmocom_VTY_Functions all;
35 import from TELNETasp_PortType all;
Philipp Maier55b90542021-07-02 12:33:19 +020036 import from StatsD_Types all;
37 import from StatsD_CodecPort all;
38 import from StatsD_CodecPort_CtrlFunct all;
39 import from StatsD_Checker all;
Philipp Maier3560bd62021-08-19 11:52:23 +020040 import from Osmocom_CTRL_Functions all;
41 import from Osmocom_CTRL_Types all;
42 import from Osmocom_CTRL_Adapter all;
Harald Welte00a067f2017-09-13 23:27:17 +020043
Philipp Maierbb7a01c2018-02-01 12:32:57 +010044 const charstring c_mgw_domain := "mgw";
45 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
46
Harald Welte21ba5572017-09-19 17:55:05 +080047 /* any variables declared in the component will be available to
48 * all functions that 'run on' the named component, similar to
49 * class members in C++ */
Philipp Maier3560bd62021-08-19 11:52:23 +020050 type component dummy_CT extends StatsD_ConnHdlr, CTRL_Adapter_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080051 port MGCP_CODEC_PT MGCP;
52 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010053 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080054 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010055
Philipp Maier2321ef92018-06-27 17:52:04 +020056 var RTP_Emulation_CT vc_RTPEM[3];
57 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010058
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020059 var OSMUX_Emulation_CT vc_OsmuxEM;
60 port OsmuxEM_CTRL_PT OsmuxEM;
61
Philipp Maier6137c322019-02-20 16:13:41 +010062 port TELNETasp_PT MGWVTY;
Philipp Maier55b90542021-07-02 12:33:19 +020063
64 var StatsD_Checker_CT vc_STATSD;
Harald Welte00a067f2017-09-13 23:27:17 +020065 };
66
Harald Weltee1e18c52017-09-17 16:23:07 +080067 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
68 var MgcpTransId tid := int2str(g_trans_id);
69 g_trans_id := g_trans_id + 1;
70 return tid;
71 }
72
Harald Welte21ba5572017-09-19 17:55:05 +080073 /* all parameters declared here can be modified / overridden by
74 * the config file in the [MODULE_PARAMETERS] section. If no
75 * config file is used or the file doesn't specify them, the
76 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080077 modulepar {
78 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020079 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020080 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080081 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020082 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020083 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010084 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020085 PortNumber mp_local_osmux_port := 1985;
Philipp Maier55b90542021-07-02 12:33:19 +020086 PortNumber mp_mgw_statsd_port := 8125;
Philipp Maier3560bd62021-08-19 11:52:23 +020087 PortNumber mp_mgw_ctrl_port := 4267;
Pau Espin Pedrole7928872022-10-07 10:57:11 +020088 /* Maximum number of available endpoints in osmo-mgw.cfg ("number endpoints"): */
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +020089 integer mp_num_endpoints := 300;
Harald Welte3c6ebb92017-09-16 00:56:57 +080090 }
91
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020092 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
93 /* Turn on conversion mode */
94 f_vty_enter_config(MGWVTY);
95 f_vty_transceive(MGWVTY, "mgcp");
96 if (osmux_on) {
97 f_vty_transceive(MGWVTY, "osmux on");
98 } else {
99 f_vty_transceive(MGWVTY, "osmux off");
100 }
101 f_vty_transceive(MGWVTY, "exit");
102 f_vty_transceive(MGWVTY, "exit");
103
104 }
105
106 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +0100107 map(self:MGWVTY, system:MGWVTY);
108 f_vty_set_prompts(MGWVTY);
109 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200110
111 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +0100112 }
113
Harald Weltebb7523b2018-03-29 08:52:01 +0200114 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
115 runs on dummy_CT {
116 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
117 map(comp_ref:RTP, system:RTP);
118 map(comp_ref:RTCP, system:RTCP);
119 comp_ref.start(RTP_Emulation.f_main());
120 }
121
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200122 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
123 runs on dummy_CT {
124 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
125 map(comp_ref:OSMUX, system:OSMUX);
126 comp_ref.start(OSMUX_Emulation.f_main());
127 }
128
Harald Welte21ba5572017-09-19 17:55:05 +0800129 /* initialization function, called by each test case at the
130 * beginning, but 'initialized' variable ensures its body is
131 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200132 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800133 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100134 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200135
Harald Welteedc45c12017-11-18 19:15:05 +0100136 if (initialized == false) {
137 initialized := true;
138
139 /* some random number for the initial transaction id */
140 g_trans_id := float2int(rnd()*65535.0);
141 map(self:MGCP, system:MGCP_CODEC_PT);
142 /* connect the MGCP test port using the given
143 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
144 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200145 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 +0200146 if (not ispresent(res.connId)) {
147 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200148 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200149 }
Harald Welteedc45c12017-11-18 19:15:05 +0100150 g_mgcp_conn_id := res.connId;
151
Harald Weltebb7523b2018-03-29 08:52:01 +0200152 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
153 f_rtpem_init(vc_RTPEM[i], i);
154 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
155 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200156 if (osmux_on) {
157 f_osmuxem_init(vc_OsmuxEM);
158 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
159 }
Philipp Maier55b90542021-07-02 12:33:19 +0200160
Philipp Maier2ff3e662021-08-19 10:52:33 +0200161 f_init_statsd("VirtCallAgent", vc_STATSD, mp_local_ipv4, mp_mgw_statsd_port);
Philipp Maier55b90542021-07-02 12:33:19 +0200162 connect(self:STATSD_PROC, vc_STATSD:STATSD_PROC);
163
164 f_statsd_reset();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800165 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800166
Philipp Maier3560bd62021-08-19 11:52:23 +0200167 f_ipa_ctrl_start_client(mp_remote_ipv4, mp_mgw_ctrl_port);
168
Harald Welteedc45c12017-11-18 19:15:05 +0100169 if (isvalue(ep)) {
170 /* do a DLCX on all connections of the EP */
171 f_dlcx_ignore(valueof(ep));
172 }
Philipp Maier6137c322019-02-20 16:13:41 +0100173
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200174 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800175 }
176
Harald Welte00a067f2017-09-13 23:27:17 +0200177 testcase TC_selftest() runs on dummy_CT {
178 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 +0100179 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 +0200180 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
181 "I: 1\n" &
182 "\n" &
183 "v=0\r\n" &
184 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
185 "s=-\r\n" &
186 "c=IN IP4 0.0.0.0\r\n" &
187 "t=0 0\r\n" &
188 "m=audio 0 RTP/AVP 126\r\n" &
189 "a=rtpmap:126 AMR/8000\r\n" &
190 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100191 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 +0200192 "M: sendrecv\r" &
193 "C: 2\r\n" &
194 "I: 1\r\n" &
195 "L: p:20, a:AMR, nt:IN\r\n" &
196 "\n" &
197 "v=0\r\n" &
198 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800199 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200200 "c=IN IP4 0.0.0.0\r\n" &
201 "t=0 0\r\n" &
202 "m=audio 4441 RTP/AVP 99\r\n" &
203 "a=rtpmap:99 AMR/8000\r\n" &
204 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800205 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200206
207 log(c_auep);
208 log(dec_MgcpCommand(c_auep));
209
210 log(c_mdcx3);
211 log(dec_MgcpCommand(c_mdcx3));
212
213 log(c_mdcx3_ret);
214 log(dec_MgcpResponse(c_mdcx3_ret));
215
216 log(c_mdcx4);
217 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800218
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100219 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
220 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 +0800221
222 log(c_crcx510_ret);
223 log(dec_MgcpResponse(c_crcx510_ret));
224 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100225
226 /* We didn't encounter any DTE, so pass the test */
227 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800228 }
229
Harald Weltee636afd2017-09-17 16:24:09 +0800230 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100231 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800232 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
233 * - CRCX with remote session description and without
234 *
235 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100236 * x packetization != 20ms
237 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800238 * x unsupported mode (517)
239 * x bidirectional mode before RemoteConnDesc: 527
240 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100241 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800242 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
243 */
244
Harald Welte21ba5572017-09-19 17:55:05 +0800245 /* build a receive template for receiving a MGCP message. You
246 * pass the MGCP response template in, and it will generate an
247 * MGCP_RecvFrom template that can match the primitives arriving on the
248 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800249 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
250 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100251 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200252 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800253 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200254 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800255 locPort := mp_local_udp_port,
256 msg := { response := resp }
257 }
258 return mrf;
259 }
260
261 /* Send a MGCP request + receive a (matching!) response */
262 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
263 var MgcpMessage msg := { command := valueof(cmd) };
264 resp.line.trans_id := cmd.line.trans_id;
265 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800266 var MGCP_RecvFrom mrf;
267 timer T := 5.0;
268
Harald Welte55015362017-11-18 16:02:42 +0100269 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800270 T.start;
271 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800272 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200273 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
274 setverdict(fail, "Response didn't match template");
275 mtc.stop;
276 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800277 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200278 [] T.timeout {
279 setverdict(fail, "Timeout waiting for response to ", cmd);
280 mtc.stop;
281 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800282 }
283 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800284
285 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
286 return mrf.msg.response;
287 } else {
288 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
289 return r;
290 }
Harald Welte00a067f2017-09-13 23:27:17 +0200291 }
292
Harald Welteba62c8c2017-11-18 18:26:49 +0100293 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
294 var integer i;
295 for (i := 0; i < lengthof(resp.params); i := i + 1) {
296 var MgcpParameter par := resp.params[i];
297 if (par.code == "I") {
298 return str2hex(par.val);
299 }
300 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200301 setverdict(fail, "Could not find conn id for MgcpReponse");
302 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100303 return '00000000'H;
304 }
305
Harald Welte10889c12017-11-18 19:40:31 +0100306 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
307 template MgcpCallId call_id := omit,
308 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100309 var template MgcpCommand cmd;
310 var MgcpResponse resp;
311 var template MgcpResponse rtmpl := {
312 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100313 code := ret_code,
314 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100315 },
316 params := *,
317 sdp := *
318 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100319 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100320 resp := mgcp_transceive_mgw(cmd, rtmpl);
321 }
322
Harald Welte10889c12017-11-18 19:40:31 +0100323 /* Send DLCX and expect OK response */
324 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
325 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100326 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100327 }
328
Harald Welteba62c8c2017-11-18 18:26:49 +0100329 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100330 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100331 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100332 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100333 }
334
Harald Weltebb7523b2018-03-29 08:52:01 +0200335 type record HostPort {
336 charstring hostname,
337 integer portnr optional
338 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200339 type record RtpOsmuxFlowData {
340 boolean local_cid_sent, /* whther non wildcarded CID was already sent to MGW */
341 MgcpOsmuxCID local_cid optional,
342 MgcpOsmuxCID remote_cid optional,
343 OsmuxemConfig cfg optional
344 }
Philipp Maierbbe454d2023-03-28 15:31:57 +0200345 type record RtpCodecDescr {
346 uint7_t pt,
347 charstring codec,
348 charstring fmtp optional
349 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200350 type record RtpFlowData {
351 HostPort em, /* emulation side */
352 HostPort mgw, /* mgw side */
Philipp Maier28bb8292018-07-20 17:09:17 +0200353 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierbbe454d2023-03-28 15:31:57 +0200354 record of RtpCodecDescr codec_descr,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100355 RtpemConfig rtp_cfg optional,
Philipp Maierbbe454d2023-03-28 15:31:57 +0200356 RtpOsmuxFlowData osmux
357 }
358
Philipp Maierd6c45592023-03-31 16:41:51 +0200359 template RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
360 charstring codec, template charstring fmtp := omit) := {
361 em := {
362 hostname := host_a,
363 portnr := omit
364 },
365 mgw := {
366 hostname := host_b,
367 portnr := omit
368 },
369 codec_descr := {{
370 pt := pt,
371 codec := codec,
372 fmtp := fmtp
373 }},
374 osmux:= {
375 local_cid_sent := false,
376 local_cid := omit,
377 remote_cid := omit,
378 cfg := omit
379 }
380 }
381
Philipp Maierbbe454d2023-03-28 15:31:57 +0200382 /* To be used with f_flow_create/modify... functions */
383 function f_gen_sdp(RtpFlowData flow) runs on dummy_CT return SDP_Message {
384 var template SDP_Message sdp;
385 var SDP_fmt_list fmt_list := {};
386 var SDP_attribute_list attributes := {};
387
388 /* Add SDP RTMAP attributes (codec type, referenced by PT) */
389 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
390 attributes := attributes & { valueof(ts_SDP_rtpmap(flow.codec_descr[i].pt,
391 flow.codec_descr[i].codec)) };
392 }
393
394 /* Add SDP PTIME attribute, regardless of which codec, the packet intervall remains the same */
395 attributes := attributes & { valueof(ts_SDP_ptime(20)) };
396
397 /* Add SDP FMTP attributes (codec parameters for each codec, referenced by PT) */
398 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
399 if (isvalue(flow.codec_descr[i].fmtp) and flow.codec_descr[i].fmtp != "") {
400 attributes := attributes & { valueof(ts_SDP_fmtp(flow.codec_descr[i].pt,
401 flow.codec_descr[i].fmtp)) };
402 }
403 }
404
405 /* Final step: Generate SDP */
406 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
407 fmt_list := fmt_list & {int2str(flow.codec_descr[i].pt)};
408 }
409 sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42", flow.em.portnr, fmt_list, attributes);
410
411 return valueof(sdp);
412 }
413
414 /* Generate a valid RTP emulation config from the payload type numbers configured in the flow description and
415 * the the default configuration of the RTP emulation module. */
416 function f_gen_rtpem_config_from_flow(RtpFlowData flow) return RtpemConfig {
417 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
418
419 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
420 var RtpemConfigPayload tx_cfg_payload;
421 var RtpemConfigPayload rx_cfg_payload;
422
423 tx_cfg_payload.payload_type := flow.codec_descr[i].pt;
424 tx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.tx_payloads[0].fixed_payload;
425 rx_cfg_payload.payload_type := flow.codec_descr[i].pt;
426 rx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.rx_payloads[0].fixed_payload;
427
428 rtp_cfg.tx_payloads := rtp_cfg.tx_payloads & {tx_cfg_payload};
429 rtp_cfg.rx_payloads := rtp_cfg.rx_payloads & {rx_cfg_payload};
430 }
431
432 return rtp_cfg;
Harald Weltebb7523b2018-03-29 08:52:01 +0200433 }
434
Philipp Maier2321ef92018-06-27 17:52:04 +0200435 /* Create an RTP flow (bidirectional, or receive-only) */
436 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 +0200437 boolean one_phase := true)
438 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200439 var template MgcpCommand cmd;
440 var MgcpResponse resp;
441
442 /* bind local RTP emulation socket */
443 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
444
Philipp Maier28bb8292018-07-20 17:09:17 +0200445 /* configure rtp-emulation */
446 if (ispresent(flow.rtp_cfg)) {
447 f_rtpem_configure(pt, flow.rtp_cfg);
448 } else {
Philipp Maierbbe454d2023-03-28 15:31:57 +0200449 f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow));
Philipp Maier28bb8292018-07-20 17:09:17 +0200450 }
451
Harald Weltebb7523b2018-03-29 08:52:01 +0200452 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200453 /* Connect flow to MGW using a CRCX that also contains an SDP
454 * part that tells the MGW where we are listening for RTP streams
455 * that come from the MGW. We get a fully working connection in
456 * one go. */
457
458 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200459 cmd.sdp := f_gen_sdp(flow);
Philipp Maierc8c0b402019-03-07 10:48:45 +0100460
Harald Weltebb7523b2018-03-29 08:52:01 +0200461 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
462 flow.mgcp_conn_id := extract_conn_id(resp);
463 /* extract port number from response */
464 flow.mgw.portnr :=
465 resp.sdp.media_list[0].media_field.ports.port_number;
466 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200467 /* Create a half-open connection only. We do not tell the MGW
468 * where it can send RTP streams to us. This means this
469 * connection will only be able to receive but can not send
470 * data back to us. In order to turn the connection in a fully
471 * bi-directional one, a separate MDCX is needed. */
472
473 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200474 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
475 flow.mgcp_conn_id := extract_conn_id(resp);
476 /* extract MGW-side port number from response */
477 flow.mgw.portnr :=
478 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200479 }
480 /* finally, connect the emulation-side RTP socket to the MGW */
481 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
482 }
483
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200484 /* Create an Osmux flow (bidirectional, or receive-only) */
485 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
486 boolean one_phase := true)
487 runs on dummy_CT {
488 var template MgcpCommand cmd;
489 var MgcpResponse resp;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200490 var OsmuxTxHandle tx_hdl;
491 var OsmuxRxHandle rx_hdl;
492 var charstring cid_response;
493 var OsmuxCID cid_resp_parsed
494
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200495 /* bind local Osmux emulation socket */
496 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
497
498 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200499 if (ispresent(flow.osmux.cfg)) {
500 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200501 } else {
502 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
503 f_osmuxem_configure(pt, osmux_cfg);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200504 flow.osmux.cfg := osmux_cfg
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200505 }
506
507 if (one_phase) {
508 /* Connect flow to MGW using a CRCX that also contains an SDP
509 * part that tells the MGW where we are listening for Osmux streams
510 * that come from the MGW. We get a fully working connection in
511 * one go. */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200512 if (flow.osmux.local_cid != -1) {
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200513 /* We may still want to negotiate osmux CID later at MDCX */
514 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200515 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200516 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200517 flow.osmux.local_cid_sent := true;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200518 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200519 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200520 cmd.sdp := f_gen_sdp(flow);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200521 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
522 flow.mgcp_conn_id := extract_conn_id(resp);
523 /* extract port number from response */
524 flow.mgw.portnr :=
525 resp.sdp.media_list[0].media_field.ports.port_number;
526 } else {
527 /* Create a half-open connection only. We do not tell the MGW
528 * where it can send Osmux streams to us. This means this
529 * connection will only be able to receive but can not send
530 * data back to us. In order to turn the connection in a fully
531 * bi-directional one, a separate MDCX is needed. */
532
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200533 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200534 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
535
536 flow.mgcp_conn_id := extract_conn_id(resp);
537 /* extract MGW-side port number from response */
538 flow.mgw.portnr :=
539 resp.sdp.media_list[0].media_field.ports.port_number;
540 }
541
542 /* extract Osmux CID we got assigned by the MGW */
543 var MgcpMessage resp_msg := {
544 response := resp
545 }
546
547 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
548 setverdict(fail, "No Osmux CID in MGCP response", resp);
549 mtc.stop;
550 }
551
552 /* Make sure response is no wildcard */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200553 flow.osmux.remote_cid := f_mgcp_osmux_cid_decode(cid_response);
554 if (flow.osmux.remote_cid == -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200555 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
556 mtc.stop;
557 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200558 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux.remote_cid));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200559 f_osmuxem_register_txhandle(pt, tx_hdl);
560
561 /* finally, connect the emulation-side RTP socket to the MGW */
562 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
563 }
564
Philipp Maier2321ef92018-06-27 17:52:04 +0200565 /* Modify an existing RTP flow */
566 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
567 runs on dummy_CT {
568 var template MgcpCommand cmd;
569 var MgcpResponse resp;
570
571 /* rebind local RTP emulation socket to the new address */
572 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
573
Philipp Maier28bb8292018-07-20 17:09:17 +0200574 /* reconfigure rtp-emulation */
575 if (ispresent(flow.rtp_cfg)) {
576 f_rtpem_configure(pt, flow.rtp_cfg);
577 } else {
Philipp Maierbbe454d2023-03-28 15:31:57 +0200578 f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow));
Philipp Maier28bb8292018-07-20 17:09:17 +0200579 }
580
Philipp Maier2321ef92018-06-27 17:52:04 +0200581 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
582 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200583 cmd.sdp := f_gen_sdp(flow);
Philipp Maier2321ef92018-06-27 17:52:04 +0200584 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
585
586 /* extract MGW-side port number from response. (usually this
587 * will not change, but thats is up to the MGW) */
588 flow.mgw.portnr :=
589 resp.sdp.media_list[0].media_field.ports.port_number;
590
591 /* reconnect the emulation-side RTP socket to the MGW */
592 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
593 }
594
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200595 /* Modify an existing Osmux flow */
596 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
597 runs on dummy_CT {
598 var template MgcpCommand cmd;
599 var MgcpResponse resp;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200600 var OsmuxRxHandle rx_hdl;
601 var charstring cid_response;
602 var OsmuxCID cid_resp_parsed
603
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200604 /* rebind local Osmux emulation socket to the new address */
605 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
606
607 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200608 if (ispresent(flow.osmux.cfg)) {
609 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200610 } else {
611 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
612 f_osmuxem_configure(pt, osmux_cfg);
613 }
614
615 /* 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 +0200616 if (flow.osmux.local_cid_sent == false and flow.osmux.local_cid != -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200617 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200618 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200619 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200620 flow.osmux.local_cid_sent := true;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200621 }
622
623 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200624 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux.local_cid);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200625 cmd.sdp := f_gen_sdp(flow);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200626 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
627
628 /* extract MGW-side port number from response. (usually this
629 * will not change, but thats is up to the MGW) */
630 flow.mgw.portnr :=
631 resp.sdp.media_list[0].media_field.ports.port_number;
632
633 /* extract Osmux CID we got assigned by the MGW */
634 var MgcpMessage resp_msg := {
635 response := resp
636 }
637
638 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
639 setverdict(fail, "No Osmux CID in MGCP response", resp);
640 mtc.stop;
641 }
642
643 /* Make sure response is no wildcard */
644 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
645 if (cid_resp_parsed == -1) {
646 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
647 mtc.stop;
648 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200649 if (cid_resp_parsed != flow.osmux.remote_cid) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200650 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
651 mtc.stop;
652 }
653
654 /* reconnect the emulation-side Osmux socket to the MGW */
655 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
656 }
657
Philipp Maier2321ef92018-06-27 17:52:04 +0200658 /* Delete an existing RTP flow */
659 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
660 runs on dummy_CT {
661 var template MgcpCommand cmd;
662 var MgcpResponse resp;
663
664 /* Switch off RTP flow */
665 f_rtpem_mode(pt, RTPEM_MODE_NONE);
666
667 /* Delete connection on MGW (if needed) */
668 if (isvalue(call_id) and isvalue(ep)) {
669 f_sleep(0.1);
670 f_dlcx_ok(valueof(ep), call_id);
671 }
672 }
673
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200674 /* Delete an existing Osmux flow */
675 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
676 runs on dummy_CT {
677 var template MgcpCommand cmd;
678 var MgcpResponse resp;
679
680 /* Switch off Osmux flow */
681 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
682
683 /* Delete connection on MGW (if needed) */
684 if (isvalue(call_id) and isvalue(ep)) {
685 f_sleep(0.1);
686 f_dlcx_ok(valueof(ep), call_id);
687 }
688 }
689
Pau Espin Pedrolb26d4462023-06-15 11:38:02 +0200690 /* Send an AuditEndpoint message to the MGW */
691 function f_auep(charstring ep_prefix) runs on dummy_CT {
692 var MgcpEndpoint ep := ep_prefix & "@" & c_mgw_domain;
693 var template MgcpCommand cmd;
694 var MgcpResponse resp;
695
696 f_init(ep);
697
698 cmd := ts_AUEP(get_next_trans_id(), ep);
699 resp := mgcp_transceive_mgw(cmd, tr_AUEP_ACK);
700 }
701
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100702 function f_crcx(charstring ep_prefix) runs on dummy_CT {
703 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800704 var template MgcpCommand cmd;
705 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100706 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800707
Harald Welteedc45c12017-11-18 19:15:05 +0100708 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800709
Harald Welteba62c8c2017-11-18 18:26:49 +0100710 /* create the connection on the MGW */
711 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100712 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100713 extract_conn_id(resp);
714
715 /* clean-up */
716 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100717 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100718
Philipp Maier45635f42018-06-05 17:28:02 +0200719 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
720 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
721 var template MgcpCommand cmd;
722 var MgcpResponse resp;
723 var MgcpCallId call_id := '1234'H;
724
725 f_init(ep);
726
727 /* create the connection on the MGW */
728 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
729 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
730 extract_conn_id(resp);
731
732 /* clean-up */
733 f_dlcx_ok(ep, call_id);
734
735 /* See also OS#2658: Even when we omit the LCO information, we
736 expect the MGW to pick a sane payload type for us. This
737 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200738 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200739 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200740 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200741 }
742
743 /* See also OS#2658: We also expect the MGW to assign a port
744 number to us. */
745 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
746 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200747 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200748 }
749 }
750
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200751 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
752 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
753 var template MgcpCommand cmd;
754 var MgcpResponse resp;
755 var MgcpCallId call_id := '1234'H;
756 var charstring cid_response;
757
758 if (run_init) {
759 f_init(ep, true);
760 }
761
762 /* create the connection on the MGW */
763 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
764 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
765 extract_conn_id(resp);
766
767 /* extract Osmux CID we got assigned by the MGW */
768 var MgcpMessage resp_msg := {
769 response := resp
770 }
771
772 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
773 setverdict(fail, "No Osmux CID in MGCP response", resp);
774 mtc.stop;
775 }
776
777 /* Make sure response is no wildcard */
778 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
779 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
780 mtc.stop;
781 }
782
783 /* clean-up */
784 f_dlcx_ok(ep, call_id);
785 }
786
Pau Espin Pedrolb26d4462023-06-15 11:38:02 +0200787 /* test valid AUEP towards "null" endpoint */
788 testcase TC_auep_null() runs on dummy_CT {
789 f_auep("null");
790 setverdict(pass);
791 }
792
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100793 /* test valid CRCX without SDP */
794 testcase TC_crcx() runs on dummy_CT {
795 f_crcx(c_mgw_ep_rtpbridge);
796 setverdict(pass);
797 }
798
Philipp Maier45635f42018-06-05 17:28:02 +0200799 /* test valid CRCX without SDP and LCO */
800 testcase TC_crcx_no_lco() runs on dummy_CT {
801 f_crcx_no_lco(c_mgw_ep_rtpbridge);
802 setverdict(pass);
803 }
804
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100805 /* test valid CRCX without SDP (older method without endpoint prefix) */
806 testcase TC_crcx_noprefix() runs on dummy_CT {
807 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800808 setverdict(pass);
809 }
810
811 /* test CRCX with unsupported mode, expect 517 */
812 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
813 var template MgcpCommand cmd;
814 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100815 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100816 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800817 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
818
Harald Welteedc45c12017-11-18 19:15:05 +0100819 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800820
Harald Welteba62c8c2017-11-18 18:26:49 +0100821 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800822 resp := mgcp_transceive_mgw(cmd, rtmpl);
823 setverdict(pass);
824 }
825
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200826 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
827 testcase TC_crcx_osmo_ign() runs on dummy_CT {
828 var template MgcpCommand cmd;
829 var MgcpResponse resp;
830 var MgcpEndpoint ep := "7@" & c_mgw_domain;
831 var MgcpCallId call_id := '3'H;
832
833 f_init(ep);
834
835 /* CRCX 1 7@mgw MGCP 1.0
836 C: 3
837 L: p:20, a:GSM-EFR, nt:IN
838 M: recvonly
839 X-Osmo-IGN: C
840 */
841
842 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
843 cmd.params := {ts_MgcpParCallId(call_id),
844 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
845 t_MgcpParConnMode("recvonly"),
846 t_MgcpParOsmoIGN("C")};
847 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
848 extract_conn_id(resp);
849
850 /* clean-up */
851 f_dlcx_ok(ep, call_id);
852 setverdict(pass);
853 }
854
Harald Welte21ba5572017-09-19 17:55:05 +0800855 /* test CRCX with early bi-directional mode, expect 527 as
856 * bi-diretional media can only be established once both local and
857 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800858 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
859 var template MgcpCommand cmd;
860 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100861 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100862 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800863 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
864
Harald Welteedc45c12017-11-18 19:15:05 +0100865 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800866
Harald Welteba62c8c2017-11-18 18:26:49 +0100867 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800868 resp := mgcp_transceive_mgw(cmd, rtmpl);
869 setverdict(pass);
870 }
871
872 /* test CRCX with unsupported Parameters */
873 testcase TC_crcx_unsupp_param() runs on dummy_CT {
874 var template MgcpCommand cmd;
875 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100876 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100877 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800878 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
879
Harald Welteedc45c12017-11-18 19:15:05 +0100880 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800881
Harald Welteba62c8c2017-11-18 18:26:49 +0100882 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100883 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
884 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
885
Harald Weltee636afd2017-09-17 16:24:09 +0800886 resp := mgcp_transceive_mgw(cmd, rtmpl);
887 setverdict(pass);
888 }
889
890 /* test CRCX with missing CallId */
891 testcase TC_crcx_missing_callid() runs on dummy_CT {
892 var template MgcpCommand cmd;
893 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100894 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100895 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800896
Harald Welteedc45c12017-11-18 19:15:05 +0100897 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800898
Harald Welteba62c8c2017-11-18 18:26:49 +0100899 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800900 cmd.params := {
901 t_MgcpParConnMode("recvonly"),
902 t_MgcpParLocConnOpt("p:20")
903 }
904 resp := mgcp_transceive_mgw(cmd, rtmpl);
905 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100906
Harald Weltee636afd2017-09-17 16:24:09 +0800907 }
908
909 /* test CRCX with missing Mode */
910 testcase TC_crcx_missing_mode() runs on dummy_CT {
911 var template MgcpCommand cmd;
912 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100913 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100914 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100915 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800916
Harald Welteedc45c12017-11-18 19:15:05 +0100917 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800918
Harald Welteba62c8c2017-11-18 18:26:49 +0100919 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800920 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100921 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800922 t_MgcpParLocConnOpt("p:20")
923 }
924 resp := mgcp_transceive_mgw(cmd, rtmpl);
925 setverdict(pass);
926 }
927
928 /* test CRCX with unsupported packetization interval */
929 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
930 var template MgcpCommand cmd;
931 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100932 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100933 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100934 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800935
Harald Welteedc45c12017-11-18 19:15:05 +0100936 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800937
Harald Welteba62c8c2017-11-18 18:26:49 +0100938 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100939 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800940 resp := mgcp_transceive_mgw(cmd, rtmpl);
941 setverdict(pass);
942 }
943
944 /* test CRCX with illegal double presence of local connection option */
945 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
946 var template MgcpCommand cmd;
947 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100948 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100949 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800950 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
951
Harald Welteedc45c12017-11-18 19:15:05 +0100952 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800953
Harald Welteba62c8c2017-11-18 18:26:49 +0100954 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100955 /* p:20 is permitted only once and not twice! */
956 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800957 resp := mgcp_transceive_mgw(cmd, rtmpl);
958 setverdict(pass);
959 }
960
961 /* test valid CRCX with valid SDP */
962 testcase TC_crcx_sdp() runs on dummy_CT {
963 var template MgcpCommand cmd;
964 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100965 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100966 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800967
Harald Welteedc45c12017-11-18 19:15:05 +0100968 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800969
Harald Welteba62c8c2017-11-18 18:26:49 +0100970 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800971 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
972 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
973 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100974 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100975
976 /* clean-up */
977 f_dlcx_ok(ep, call_id);
978
Harald Weltee636afd2017-09-17 16:24:09 +0800979 setverdict(pass);
980 }
981
Philipp Maier5e06cee2018-02-01 18:28:08 +0100982 /* test valid wildcarded CRCX */
983 testcase TC_crcx_wildcarded() runs on dummy_CT {
984 var template MgcpCommand cmd;
985 var MgcpResponse resp;
986 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
987 var MgcpCallId call_id := '1234'H;
988 var MgcpEndpoint ep_assigned;
989 f_init();
990
991 /* create the connection on the MGW */
992 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
993 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
994 extract_conn_id(resp);
995
996 /* extract endpoint name we got assigned by the MGW */
997 var MgcpMessage resp_msg := {
998 response := resp
999 }
1000 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
1001 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001002 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +01001003 }
1004
1005 /* clean-up */
1006 f_dlcx_ok(ep_assigned, call_id);
1007
1008 setverdict(pass);
1009 }
1010
1011 /* test valid wildcarded CRCX */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001012 type record of MgcpEndpoint MgcpEndpointList;
Philipp Maier5e06cee2018-02-01 18:28:08 +01001013 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maier5e06cee2018-02-01 18:28:08 +01001014 var integer i;
1015 var template MgcpCommand cmd;
1016 var MgcpResponse resp;
1017 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1018 var MgcpCallId call_id := '1234'H;
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001019 var MgcpEndpoint ep_assigned;
1020 var MgcpEndpointList ep_assigned_li := {};
Philipp Maier5e06cee2018-02-01 18:28:08 +01001021 f_init();
1022
1023 /* Exhaust all endpoint resources on the virtual trunk */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001024 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier5e06cee2018-02-01 18:28:08 +01001025 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1026 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1027
1028 /* Make sure we got a connection id */
1029 extract_conn_id(resp);
1030
1031 var MgcpMessage resp_msg := {
1032 response := resp
1033 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001034 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
Philipp Maier5e06cee2018-02-01 18:28:08 +01001035 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001036 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +01001037 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001038 ep_assigned_li := ep_assigned_li & {ep_assigned}
Philipp Maier5e06cee2018-02-01 18:28:08 +01001039 }
1040
1041 /* Try to allocate one more endpoint, which should fail */
1042 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1043 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
1044 resp := mgcp_transceive_mgw(cmd, rtmpl);
1045 setverdict(pass);
1046
1047 /* clean-up */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001048 for (i := 0; i < mp_num_endpoints; i := i+1) {
1049 f_dlcx_ok(ep_assigned_li[i], call_id);
Philipp Maier5e06cee2018-02-01 18:28:08 +01001050 }
1051 setverdict(pass);
1052 }
1053
Harald Weltee636afd2017-09-17 16:24:09 +08001054 /* TODO: various SDP related bits */
1055
1056
1057 /* TODO: CRCX with X-Osmux */
1058 /* TODO: double CRCX without force_realloc */
1059
1060 /* TODO: MDCX (various) */
1061
1062 /* TODO: MDCX without CRCX first */
1063 testcase TC_mdcx_without_crcx() runs on dummy_CT {
1064 var template MgcpCommand cmd;
1065 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001066 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +01001067 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +08001068 var template MgcpResponse rtmpl := {
1069 line := {
1070 /* TODO: accept/enforce better error? */
1071 code := "400",
1072 string := ?
1073 },
1074 params:= { },
1075 sdp := omit
1076 };
1077
Harald Welteedc45c12017-11-18 19:15:05 +01001078 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001079
Harald Welte2bcfd3a2017-11-18 22:14:35 +01001080 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001081 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1082 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1083 valueof(ts_SDP_ptime(20)) });
1084 resp := mgcp_transceive_mgw(cmd, rtmpl);
1085 setverdict(pass);
1086 }
1087
1088 /* DLCX without CRCX first */
1089 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1090 var template MgcpCommand cmd;
1091 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001092 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001093 var template MgcpResponse rtmpl := {
1094 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001095 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001096 string := ?
1097 },
1098 params:= { },
1099 sdp := omit
1100 };
1101
Harald Welteedc45c12017-11-18 19:15:05 +01001102 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001103
Harald Welteedc45c12017-11-18 19:15:05 +01001104 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001105 resp := mgcp_transceive_mgw(cmd, rtmpl);
1106 setverdict(pass);
1107 }
1108
Philipp Maier21c1cff2021-07-20 14:22:53 +02001109 /* DLCX to non existing endpoint */
1110 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1111 var template MgcpCommand cmd;
1112 var MgcpResponse resp;
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001113 var charstring non_existant_ep := hex2str(int2hex(mp_num_endpoints + 1, 4))
1114 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & non_existant_ep & "@" & c_mgw_domain;
Philipp Maier21c1cff2021-07-20 14:22:53 +02001115 var template MgcpResponse rtmpl := {
1116 line := {
1117 code := ("500"),
1118 string := ?
1119 },
1120 params:= { },
1121 sdp := omit
1122 };
1123
1124 f_init(ep);
1125
1126 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1127 resp := mgcp_transceive_mgw(cmd, rtmpl);
1128 setverdict(pass);
1129 }
1130
Philipp Maier8a3dc922018-02-02 14:55:12 +01001131 /* test valid wildcarded MDCX */
1132 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1133 /* Note: A wildcarded MDCX is not allowed, so we expect the
1134 * MGW to reject this request */
1135 var template MgcpCommand cmd;
1136 var MgcpResponse resp;
1137 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1138 var MgcpCallId call_id := '1225'H;
1139 var template MgcpResponse rtmpl := {
1140 line := {
1141 /* TODO: accept/enforce better error? */
1142 code := "507",
1143 string := ?
1144 },
1145 params:= { },
1146 sdp := omit
1147 };
1148
1149 f_init(ep);
1150
1151 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1152 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1153 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1154 valueof(ts_SDP_ptime(20)) });
1155 resp := mgcp_transceive_mgw(cmd, rtmpl);
1156 setverdict(pass);
1157 }
1158
1159 /* test valid wildcarded DLCX */
1160 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001161 var template MgcpCommand cmd;
1162 var MgcpResponse resp;
1163 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001164 var integer i;
1165 var MgcpCallId call_id := '1234'H;
1166 var StatsDExpects expect;
1167 f_init(ep);
1168
1169 /* Allocate a few endpoints */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001170 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier55b90542021-07-02 12:33:19 +02001171 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1172 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1173 }
1174
Philipp Maier1298b092021-11-18 18:33:39 +01001175 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
1176 * occupied endpoints */
1177 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001178 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001179 { 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 +02001180 };
1181 f_statsd_expect(expect);
1182
1183 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001184 var template MgcpResponse rtmpl := {
1185 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001186 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001187 string := ?
1188 },
1189 params:= { },
1190 sdp := omit
1191 };
Philipp Maier55b90542021-07-02 12:33:19 +02001192 cmd := ts_DLCX(get_next_trans_id(), ep);
1193 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001194
Philipp Maier6c740e82022-06-30 12:04:34 +02001195 /* Query a the statsd once to ensure that intermediate results are pulled from the
1196 * pipeline. The second query (below) will return the actual result. */
1197 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001198 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := mp_num_endpoints}
Philipp Maier6c740e82022-06-30 12:04:34 +02001199 };
1200 f_statsd_expect(expect);
1201
1202 /* The second query must resturn a result with 0 endpoints in use. */
Philipp Maier55b90542021-07-02 12:33:19 +02001203 expect := {
1204 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1205 };
1206 f_statsd_expect(expect);
1207
Philipp Maier8a3dc922018-02-02 14:55:12 +01001208 setverdict(pass);
1209 }
1210
Harald Welte79181ff2017-11-18 19:26:11 +01001211 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1212 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1213 var template MgcpCommand cmd;
1214 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001215 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001216 var MgcpCallId call_id := '51234'H;
1217
1218 f_init(ep);
1219
1220 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1221 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1222
1223 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1224
1225 setverdict(pass);
1226 }
1227
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001228 /* test valid CRCX without SDP */
1229 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1230 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1231 setverdict(pass);
1232 }
1233
1234 /* test valid CRCX without SDP */
1235 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1236 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1237 setverdict(pass);
1238 }
1239
1240 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1241 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1242 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1243 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1244 setverdict(pass);
1245 }
1246
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02001247 /* test Creating 257 concurrent osmux conns. It should fail since maximum is 256. */
1248 testcase TC_crcx_osmux_257() runs on dummy_CT {
1249 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1250 var template MgcpCommand cmd;
1251 var MgcpResponse resp;
1252 var charstring cid_response;
1253 var integer i;
1254
1255 f_init(ep, true);
1256
1257 for (i := 0; i < 256; i := i + 1) {
1258
1259 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1260 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
1261 extract_conn_id(resp);
1262
1263 /* extract Osmux CID we got assigned by the MGW */
1264 var MgcpMessage resp_msg := {
1265 response := resp
1266 }
1267
1268 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
1269 setverdict(fail, "No Osmux CID in MGCP response", resp);
1270 mtc.stop;
1271 }
1272
1273 /* Make sure response is no wildcard */
1274 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
1275 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
1276 mtc.stop;
1277 }
1278 }
1279
1280 /* Now conn num 257, it should fail due to all Osmux conns already allocated: */
1281 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1282 resp := mgcp_transceive_mgw(cmd, tr_MgcpResp_Err("400"));
1283
1284 setverdict(pass);
1285
1286 /* Clean up */
1287 for (i := 0; i < 256; i := i + 1) {
1288 f_dlcx_ok(ep, int2hex(i, 4));
1289 }
1290 }
1291
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001292 /* Create one half open connection in receive-only mode. The MGW must accept
1293 * the packets but must not send any. */
1294 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1295 var RtpFlowData flow;
1296 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1297 var MgcpCallId call_id := '1225'H;
1298 var OsmuxemStats stats;
1299 var OsmuxTxHandle tx_hdl;
1300
1301 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001302 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001303 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001304 flow.osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001305 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1306
1307 /* create a transmitter not yet known by MGW */
1308 tx_hdl := valueof(t_TxHandleAMR590(2));
1309 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1310
1311 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1312 f_sleep(1.0);
1313 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1314
1315 stats := f_osmuxem_stats_get(OsmuxEM);
1316
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001317 if (stats.num_pkts_tx < 40 / flow.osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001318 setverdict(fail);
1319 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001320 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 +02001321 setverdict(fail);
1322 }
1323
1324 f_osmuxem_stats_err_check(stats);
1325
1326 setverdict(pass);
1327 }
1328
1329 /* Create one connection in loopback mode, test if the Osmux packets are
1330 * actually reflected */
1331 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1332 var RtpFlowData flow;
1333 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1334 var MgcpCallId call_id := '1225'H;
1335 var OsmuxemStats stats;
1336 var OsmuxTxHandle tx_hdl;
1337
1338 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001339 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001340 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001341 flow.osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001342 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1343
1344 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1345 f_sleep(1.0);
1346
1347 /* Switch off both Tx, wait to receive delayed frames from MGW */
1348 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1349 f_sleep(0.1);
1350 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1351
1352 stats := f_osmuxem_stats_get(OsmuxEM);
1353
1354 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1355 setverdict(fail);
1356 }
1357 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1358 setverdict(fail);
1359 }
1360
1361 f_osmuxem_stats_err_check(stats);
1362
1363 setverdict(pass);
1364 }
1365
1366 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1367 * must match the reception statistics on the other side and vice versa. The
1368 * user may also supply a tolerance value (number of packets) when deviations
1369 * are acceptable */
1370 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1371 var integer plen;
1372
1373 log("stats A: ", a);
1374 log("stats B: ", b);
1375 log("tolerance: ", tolerance, " packets");
1376 log("batch_size: ", batch_size, " packets");
1377
1378 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1379
1380 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1381 return false;
1382 }
1383
1384 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1385 return false;
1386 }
1387
1388 if(a.num_pkts_tx > 0) {
1389 plen := a.bytes_payload_tx / a.num_pkts_tx;
1390 } else {
1391 plen := 0;
1392 }
1393
1394 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1395 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1396 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1397 return false;
1398 }
1399
1400 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) {
1401 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1402 return false;
1403 }
1404
1405 return true;
1406 }
1407
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001408 function f_TC_two_crcx_and_rtp_osmux(boolean bidir, boolean rtp_amr_oa,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001409 charstring local_ip_rtp, charstring remote_ip_rtp,
1410 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001411 var RtpFlowData flow[2];
1412 var RtpemStats stats_rtp;
1413 var OsmuxemStats stats_osmux;
1414 var MgcpResponse resp;
1415 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1416 var MgcpCallId call_id := '1226'H;
1417 var integer tolerance := 0;
1418
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001419 var octetstring amr_payload;
1420 var charstring fmtp;
1421
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001422 f_init(ep, true);
1423
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001424 var AMRFT cmr := AMR_FT_0;
1425 var AMRFT ft := AMR_FT_2;
1426 if (rtp_amr_oa) {
1427 fmtp := "octet-align=1";
1428 var RTP_AMR_Hdr amr_oa_hdr := valueof(ts_RTP_AMR_Hdr(enum2int(cmr), enum2int(ft)));
1429 amr_payload := enc_RTP_AMR_Hdr(amr_oa_hdr) &
1430 f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload);
1431 } else {
1432 fmtp := "octet-align=0";
1433 /* Convert OA to BWE: */
1434 var RTP_AMR_BWE_Hdr amr_bwe_hdr := valueof(ts_RTP_AMR_BWE_Hdr(enum2int(cmr), enum2int(ft)));
1435 var bitstring amr_bwe_hdr_bits := substr(oct2bit(enc_RTP_AMR_BWE_Hdr(amr_bwe_hdr)), 0 , 10);
1436 var bitstring amr_data_bits := oct2bit(f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload));
1437 var bitstring amr_payload_bits := amr_bwe_hdr_bits & substr(amr_data_bits, 0, f_amrft_payload_bits_len(enum2int(ft)));
1438 amr_payload := bit2oct(f_pad_bit(amr_payload_bits, (lengthof(amr_payload_bits)+7)/8*8, '0'B));
1439 };
1440
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001441 /* from us to MGW */
Philipp Maier6b41e152023-03-31 11:45:24 +02001442 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000", fmtp));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001443 flow[0].rtp_cfg := c_RtpemDefaultCfg
Philipp Maierbbe454d2023-03-28 15:31:57 +02001444 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1445 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := amr_payload;
1446 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1447 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := amr_payload;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001448 /* bind local RTP emulation sockets */
1449 flow[0].em.portnr := 10000;
1450 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1451
1452 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001453 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001454 flow[1].em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001455 flow[1].osmux.local_cid := 2;
1456 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001457 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1458
1459 if (bidir) {
1460 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1461 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1462
1463 /* Note: When we test bidirectional we may
1464 * loose packets during switch off because
1465 * both ends are transmitting and we only
1466 * can switch them off one by one. */
1467 tolerance := 3;
1468 } else {
1469 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1470 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1471 }
1472
1473 f_sleep(1.0);
1474
1475 /* Switch off both Tx, wait to receive delayed frames from MGW */
1476 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1477 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1478 f_sleep(0.1);
1479
1480 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1481 f_flow_delete(RTPEM[1]);
1482
1483 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1484 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001485 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 +02001486 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1487 mtc.stop;
1488 }
1489
1490 f_rtpem_stats_err_check(stats_rtp);
1491 f_osmuxem_stats_err_check(stats_osmux);
1492
1493 setverdict(pass);
1494 }
1495
1496 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1497 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001498 f_TC_two_crcx_and_rtp_osmux(false, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001499 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001500 }
1501
1502 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1503 * exchange some data in both directions */
1504 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001505 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001506 mp_local_ipv4, mp_remote_ipv4);
1507 }
1508
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001509 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1510 * exchange some data in both directions. RTP side is configured to
1511 * rx/rx AMR in bandwidth-efficient mode. */
1512 testcase TC_two_crcx_and_rtp_osmux_bidir_amr_bwe() runs on dummy_CT {
1513 f_TC_two_crcx_and_rtp_osmux(true, false, mp_local_ipv4, mp_remote_ipv4,
1514 mp_local_ipv4, mp_remote_ipv4);
1515 }
1516
1517
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001518 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1519 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001520 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001521 mp_local_ipv6, mp_remote_ipv6);
1522 }
1523 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1524 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001525 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001526 mp_local_ipv6, mp_remote_ipv6);
1527 }
1528 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1529 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001530 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001531 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001532 }
1533
1534
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001535 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1536 charstring local_ip_rtp, charstring remote_ip_rtp,
1537 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001538 var RtpFlowData flow[2];
1539 var RtpemStats stats_rtp;
1540 var OsmuxemStats stats_osmux;
1541 var MgcpResponse resp;
1542 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1543 var MgcpCallId call_id := '1227'H;
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001544 var integer num_pkts_tx[2], num_pkts_rx[2];
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001545 var integer temp;
1546
1547 f_init(ep, true);
1548
1549 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001550 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001551 flow[0].rtp_cfg := c_RtpemDefaultCfg
Philipp Maierbbe454d2023-03-28 15:31:57 +02001552 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1553 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001554 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
Philipp Maierbbe454d2023-03-28 15:31:57 +02001555 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload);
1556 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := flow[0].rtp_cfg.rx_payloads[0].fixed_payload;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001557 /* bind local RTP emulation sockets */
1558 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001559 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001560
1561
1562 /* Create the second connection. This connection will be also
1563 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001564 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001565 flow[1].em.portnr := mp_local_osmux_port;
1566 if (crcx_osmux_wildcard) {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001567 flow[1].osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001568 } else {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001569 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001570 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001571 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001572 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001573
1574
1575 /* The first leg starts transmitting */
1576 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1577 f_sleep(0.5);
1578 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1579 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1580 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1581 mtc.stop;
1582 }
1583 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1584 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1585 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1586 mtc.stop;
1587 }
1588
1589 /* The second leg starts transmitting a little later */
1590 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1591 f_sleep(1.0);
1592 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1593 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1594 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1595 mtc.stop;
1596 }
1597 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1598 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1599 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1600 mtc.stop;
1601 }
1602
1603 /* The first leg will now be switched into bidirectional
1604 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001605 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001606 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001607 /* At this point in time, flow[1](Osmux)->MGW->flow[0](RTP) is active,
1608 * hence if local CID was provided during CRCX we should already be seeing packets
1609 * flowing in one direction, aka stats_rtp.num_pkts_rx sould be >0 after a while: */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001610 f_sleep(0.5);
1611 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1612 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1613 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1614 mtc.stop;
1615 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001616 if (not crcx_osmux_wildcard and stats_rtp.num_pkts_rx == 0) {
1617 setverdict(fail, "received 0 packets Osmux->MGW->RTP");
1618 mtc.stop;
1619 }
1620
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001621 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1622 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1623 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1624 mtc.stop;
1625 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001626 if (stats_osmux.num_pkts_rx > 0) {
1627 setverdict(fail, "received unexpected ", stats_osmux.num_pkts_rx, " packets RTP->MGW->Osmux");
1628 mtc.stop;
1629 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001630
1631 /* When the second leg is switched into bidirectional mode
1632 * as well, then the MGW will connect the two together and
1633 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001634 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001635 if (crcx_osmux_wildcard) {
Pau Espin Pedrol982b9792022-10-07 11:48:13 +02001636 /* We set now the local CID in MDCX: */
1637 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001638 }
1639 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001640 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1641 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1642 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
1643 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
1644 num_pkts_rx[0] := stats_rtp.num_pkts_rx;
1645 num_pkts_rx[1] := stats_osmux.num_pkts_rx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001646 f_sleep(2.0);
1647
1648 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1649 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1650
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001651 temp := (stats_rtp.num_pkts_tx - num_pkts_tx[0]) -
1652 (stats_osmux.num_pkts_rx - num_pkts_rx[1]) * flow[1].osmux.cfg.batch_size;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001653 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 +02001654 log("stats_rtp: ", stats_rtp);
1655 log("stats_osmux: ", stats_osmux);
1656 log("old_rtp_tx: ", num_pkts_tx[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001657 setverdict(fail, "RTP-Tx vs OSmux-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001658 mtc.stop;
1659 }
1660
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001661 temp := (stats_osmux.num_pkts_tx - num_pkts_tx[1]) -
1662 ((stats_rtp.num_pkts_rx - num_pkts_rx[0])/ flow[1].osmux.cfg.batch_size);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001663 if (temp > 3 or temp < -3) {
Pau Espin Pedroldb1e0682022-10-06 19:40:29 +02001664 log("stats_rtp: ", stats_rtp);
1665 log("stats_osmux: ", stats_osmux);
1666 log("old_osmux_tx: ", num_pkts_tx[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001667 setverdict(fail, "Osmux-Tx vs RTP-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001668 mtc.stop;
1669 }
1670
1671 f_rtpem_stats_err_check(stats_rtp);
1672 f_osmuxem_stats_err_check(stats_osmux);
1673
1674 /* Tear down */
1675 f_flow_delete(RTPEM[0]);
1676 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1677 setverdict(pass);
1678 }
1679
1680 /* create one RTP and one OSmux emulations and pass data in both
1681 directions. Create CRCX with wildcard Osmux CID and set it later
1682 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1683 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001684 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1685 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001686 }
1687
1688 /* create one RTP and one OSmux emulations and pass data in both
1689 directions. Create CRCX with fixed Osmux CID and keep it during
1690 MDCX. This is similar to how BSC sets up the call in AoIP. */
1691 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001692 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1693 mp_local_ipv4, mp_remote_ipv4);
1694 }
1695
1696 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1697 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1698 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1699 mp_local_ipv6, mp_remote_ipv6);
1700 }
1701 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1702 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1703 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1704 mp_local_ipv6, mp_remote_ipv6);
1705 }
1706 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1707 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1708 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1709 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001710 }
1711
Harald Welte646ecdb2017-12-28 03:21:57 +01001712 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1713 var template MgcpCommand cmd;
1714 var MgcpResponse resp;
1715
1716 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1717 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1718
1719 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1720
1721 setverdict(pass);
1722 }
1723
1724 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1725 var MgcpEndpoint ep;
1726 var MgcpCallId call_id;
1727 var integer ep_nr;
1728
1729 f_init();
1730
1731 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001732 if(ep_nr > 15) {
1733 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1734 } else {
1735 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1736 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001737 call_id := int2hex(ep_nr, 2) & '1234'H;
1738 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1739 }
1740 }
1741
Harald Welte79181ff2017-11-18 19:26:11 +01001742 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1743 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001744 var template MgcpCommand cmd;
1745 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001746 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001747 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001748
Harald Welteedc45c12017-11-18 19:15:05 +01001749 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001750
Harald Welteba62c8c2017-11-18 18:26:49 +01001751 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001752 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001753
Harald Welteba62c8c2017-11-18 18:26:49 +01001754 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001755
1756 setverdict(pass);
1757 }
1758
Harald Welte79181ff2017-11-18 19:26:11 +01001759 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1760 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1761 var template MgcpCommand cmd;
1762 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001763 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001764 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001765
1766 f_init(ep);
1767
1768 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1769 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1770
1771 f_dlcx_ok(ep);
1772
1773 setverdict(pass);
1774 }
1775
1776
Harald Welte6d167f82017-11-18 19:41:35 +01001777 /* CRCX + DLCX of valid endpoint but invalid call-id */
1778 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1779 var template MgcpCommand cmd;
1780 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001781 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001782 var MgcpCallId call_id := '51231'H;
1783
1784 f_init(ep);
1785
1786 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1787 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1788
1789 f_dlcx(ep, "516", *, 'ffff'H);
1790
1791 setverdict(pass);
1792 }
1793
1794
1795 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1796 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1797 var template MgcpCommand cmd;
1798 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001799 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001800 var MgcpCallId call_id := '51230'H;
1801
1802 f_init(ep);
1803
1804 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1805 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1806
1807 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1808
1809 setverdict(pass);
1810 }
1811
1812
Harald Weltee636afd2017-09-17 16:24:09 +08001813 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001814 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1815 var template MgcpCommand cmd;
1816 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001817 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001818 var MgcpCallId call_id := '51229'H;
1819 var template MgcpResponse rtmpl := {
1820 line := {
1821 code := "200",
1822 string := "OK"
1823 },
1824 params:= { },
1825 sdp := omit
1826 };
1827
1828 f_init(ep);
1829
1830 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1831 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1832
1833 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1834 resp := mgcp_transceive_mgw(cmd, rtmpl);
1835 resp := mgcp_transceive_mgw(cmd, rtmpl);
1836
1837 setverdict(pass);
1838 }
1839
Harald Weltebb7523b2018-03-29 08:52:01 +02001840 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1841 testcase TC_rtpem_selftest() runs on dummy_CT {
1842 var RtpemStats stats[2];
1843 var integer local_port := 10000;
1844 var integer local_port2 := 20000;
1845
1846 f_init();
1847
1848 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1849 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1850
1851 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1852 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1853
1854 log("=== starting");
1855 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1856 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1857
1858 f_sleep(5.0);
1859
1860 log("=== stopping");
1861 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1862 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1863 f_sleep(0.5);
1864 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1865 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1866
1867 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1868 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1869 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1870 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001871 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001872 }
1873 setverdict(pass);
1874 }
1875
Philipp Maier2321ef92018-06-27 17:52:04 +02001876 /* Create one half open connection in receive-only mode. The MGW must accept
1877 * the packets but must not send any. */
1878 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1879 var RtpFlowData flow;
1880 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1881 var MgcpCallId call_id := '1225'H;
1882 var RtpemStats stats;
1883
1884 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001885 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001886 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001887 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001888
1889 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1890 f_sleep(1.0);
1891 f_flow_delete(RTPEM[0], ep, call_id);
1892
1893 stats := f_rtpem_stats_get(RTPEM[0]);
1894
Philipp Maier42b17cc2019-10-01 13:53:17 +02001895 /* Make sure that at least some amount of RTP packets/bytes
1896 * have has been transmitted. The compare values for
1897 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1898 * using a testrun and the results were devided by 2, so even
1899 * in load situations we should reach the minimum amount of
1900 * required packets/bytes */
1901
1902 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001903 setverdict(fail);
1904 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001905 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001906 setverdict(fail);
1907 }
Philipp Maier36291392018-07-25 09:40:44 +02001908
1909 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001910
1911 setverdict(pass);
1912 }
1913
1914 /* Create one connection in loopback mode, test if the RTP packets are
1915 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001916 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 +02001917 var RtpFlowData flow;
1918 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1919 var MgcpCallId call_id := '1225'H;
1920 var RtpemStats stats;
1921
1922 f_init(ep);
Philipp Maier47ec8122023-05-23 11:38:30 +02001923 flow := valueof(t_RtpFlow(local_ip, remote_ip, 112, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001924 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001925 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001926
1927 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1928 f_sleep(1.0);
1929 f_flow_delete(RTPEM[0], ep, call_id);
1930
1931 stats := f_rtpem_stats_get(RTPEM[0]);
1932
1933 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1934 setverdict(fail);
1935 }
1936 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1937 setverdict(fail);
1938 }
Philipp Maier36291392018-07-25 09:40:44 +02001939
1940 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001941
1942 setverdict(pass);
1943 }
1944
Philipp Maier1ac13982021-05-07 23:06:07 +02001945 /* Create one connection in loopback mode, test if the RTP packets are
1946 * actually reflected */
1947 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001948 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001949 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001950 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1951 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1952 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001953
1954 /* Same as above, but we will intenionally not tell the MGW where to
1955 * send the outgoing traffic. The connection is still created in
1956 * loopback mode, so the MGW should take the originating address from
1957 * the incoming RTP packet and send it back to the source */
1958 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001959 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001960 }
1961
1962
Philipp Maier7df85f62018-07-25 10:26:09 +02001963 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1964 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001965 var RtpFlowData flow[2];
1966 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001967 var MgcpResponse resp;
1968 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1969 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001970 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001971
1972 f_init(ep);
1973
1974 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001975 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001976 /* bind local RTP emulation sockets */
1977 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001978 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001979
1980 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001981 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001982 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001983 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1984
1985 if (bidir) {
1986 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1987 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1988
1989 /* Note: When we test bidirectional we may
1990 * loose packets during switch off because
1991 * both ends are transmitting and we only
1992 * can switch them off one by one. */
1993 tolerance := 3;
1994 } else {
1995 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1996 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1997 }
1998
1999 f_sleep(1.0);
2000
2001 f_flow_delete(RTPEM[1]);
2002 f_flow_delete(RTPEM[0], ep, call_id);
2003
2004 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2005 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2006 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
2007 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002008 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002009 }
2010
Philipp Maier36291392018-07-25 09:40:44 +02002011 f_rtpem_stats_err_check(stats[0]);
2012 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02002013
Philipp Maier2321ef92018-06-27 17:52:04 +02002014 setverdict(pass);
2015 }
2016
2017 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
2018 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02002019 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02002020 }
2021
2022 /* create two local RTP emulations; create two connections on MGW EP,
2023 * exchange some data in both directions */
2024 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02002025 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
2026 }
2027
2028 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
2029 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
2030 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
2031 }
2032
2033 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
2034 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
2035 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02002036 }
2037
2038 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002039 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002040 charstring local_ip_b, charstring remote_ip_b,
2041 uint7_t payload_type := 3,
2042 charstring codec_name := "GSM/8000/1") runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02002043 var RtpFlowData flow[2];
2044 var RtpemStats stats[2];
2045 var MgcpResponse resp;
2046 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2047 var MgcpCallId call_id := '1227'H;
2048 var integer num_pkts_tx[2];
2049 var integer temp;
2050
2051 f_init(ep);
2052
2053 /* Create the first connection in receive only mode */
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002054 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, payload_type, codec_name));
Philipp Maier2321ef92018-06-27 17:52:04 +02002055 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002056 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02002057
2058 /* Create the second connection. This connection will be also
2059 * in receive only mode */
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002060 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, payload_type, codec_name));
Philipp Maier2321ef92018-06-27 17:52:04 +02002061 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002062 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02002063
2064 /* The first leg starts transmitting */
2065 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2066 f_sleep(0.5);
2067 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2068 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002069 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002070 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002071 }
2072 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2073 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002074 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002075 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002076 }
2077
2078 /* The second leg starts transmitting a little later */
2079 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2080 f_sleep(1.0);
2081 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2082 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002083 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002084 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002085 }
2086 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2087 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002088 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002089 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002090 }
2091
2092 /* The first leg will now be switched into bidirectional
2093 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002094 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2095 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2096 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002097 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2098 f_sleep(0.5);
2099 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002100 if (stats[0].num_pkts_rx_err_disabled != 0) {
2101 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002102 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002103 }
2104 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2105 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002106 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002107 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002108 }
2109
2110 /* When the second leg is switched into bidirectional mode
2111 * as well, then the MGW will connect the two together and
2112 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02002113 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2114 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002115 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002116 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2117 f_sleep(2.0);
2118
2119 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2120 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2121
2122 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2123 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002124 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002125 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002126 }
2127
2128 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2129 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002130 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002131 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002132 }
Philipp Maier36291392018-07-25 09:40:44 +02002133
2134 f_rtpem_stats_err_check(stats[0]);
2135 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02002136
2137 /* Tear down */
2138 f_flow_delete(RTPEM[0]);
2139 f_flow_delete(RTPEM[1], ep, call_id);
2140 setverdict(pass);
2141 }
2142
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002143 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
2144 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2145 mp_local_ipv4, mp_remote_ipv4);
2146 }
2147
2148 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
2149 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
2150 mp_local_ipv6, mp_remote_ipv6);
2151 }
2152
2153 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
2154 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2155 mp_local_ipv6, mp_remote_ipv6);
2156 }
2157
Oliver Smith871f45a2023-01-24 16:21:50 +01002158 testcase TC_two_crcx_mdcx_and_rtp_clearmode() runs on dummy_CT {
2159 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2160 mp_local_ipv4, mp_remote_ipv4,
2161 120, /* 3GPP TS 48.103 table 5.4.2.2.1 */
2162 "CLEARMODE/8000");
2163 }
2164
Philipp Maier2321ef92018-06-27 17:52:04 +02002165 /* Test what happens when two RTP streams from different sources target
2166 * a single connection. Is the unsolicited stream properly ignored? */
2167 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2168 var RtpFlowData flow[2];
2169 var RtpemStats stats[2];
2170 var MgcpResponse resp;
2171 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2172 var MgcpCallId call_id := '1234321326'H;
2173 var integer unsolicited_port := 10002;
2174
2175 f_init(ep);
2176
2177 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002178 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002179 /* bind local RTP emulation sockets */
2180 flow[0].em.portnr := 10000;
2181 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2182
2183 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002184 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002185 flow[1].em.portnr := 20000;
2186 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002187
2188 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2189 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2190
Philipp Maier2321ef92018-06-27 17:52:04 +02002191 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002192
Philipp Maier2321ef92018-06-27 17:52:04 +02002193 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002194 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2195 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002196 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2197
2198 f_sleep(0.5);
2199
Daniel Willmanna069d382018-12-13 13:53:33 +01002200 /* Stop transmitting packets and tear down the flows */
2201 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002202 f_flow_delete(RTPEM[0]);
2203 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002204
2205 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2206 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2207 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2208 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002209 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002210 }
2211
Philipp Maier36291392018-07-25 09:40:44 +02002212 f_rtpem_stats_err_check(stats[0]);
2213 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002214
Harald Weltebb7523b2018-03-29 08:52:01 +02002215 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002216 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002217
Philipp Maier2321ef92018-06-27 17:52:04 +02002218 /* Test a handover situation. We first create two connections transmit
2219 * some data bidirectionally. Then we will simulate a handover situation. */
2220 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2221 var RtpFlowData flow[2];
2222 var RtpemStats stats[3];
2223 var MgcpResponse resp;
2224 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2225 var MgcpCallId call_id := '76338'H;
2226 var integer port_old;
2227
2228 f_init(ep);
2229
2230 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002231 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002232 /* bind local RTP emulation sockets */
2233 flow[0].em.portnr := 10000;
2234 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2235
2236 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002237 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002238 flow[1].em.portnr := 20000;
2239 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2240
2241 /* Normal rtp flow for one second */
2242 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2243 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2244 f_sleep(1.0);
2245
2246 /* Now switch the flow over to a new port (BTS) */
2247 port_old := flow[0].em.portnr;
2248 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002249 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002250 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002251 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002252
2253 /* When handing over a call, the old source may still keep
2254 * transmitting for a while. We simulate this by injecting
2255 * some unsolicited packets on the behalf of the old source,
2256 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002257 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2258 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002259 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2260 f_sleep(1.0);
2261 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2262 f_sleep(1.0);
2263
2264 /* Terminate call */
2265 f_flow_delete(RTPEM[0]);
2266 f_flow_delete(RTPEM[1], ep, call_id);
2267
2268 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2269 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2270 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2271 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002272 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002273 }
2274 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2275 if (stats[2].num_pkts_rx_err_disabled != 0) {
2276 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002277 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002278 }
2279
Philipp Maier36291392018-07-25 09:40:44 +02002280 f_rtpem_stats_err_check(stats[0]);
2281 f_rtpem_stats_err_check(stats[1]);
2282 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002283
Philipp Maier2321ef92018-06-27 17:52:04 +02002284 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002285 }
Harald Weltef53f1642017-11-18 19:57:11 +01002286
Andreas Eversbergdb99cb02023-07-12 11:44:11 +02002287 testcase TC_two_crcx_confecho_sendonly_rtp() runs on dummy_CT {
2288 var RtpFlowData flow[2];
2289 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2290 var MgcpCallId call_id := '1225'H;
2291 var RtpemStats stats[2];
2292
2293 f_init(ep);
2294
2295 /* "Talker" is sending to MGW and receives echo. */
2296 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "GSM-HR-08/8000/1"));
2297 flow[0].em.portnr := 10000;
2298 f_flow_create(RTPEM[0], ep, call_id, "confecho", flow[0]);
2299
2300 /* "Listener" receives from MGW. */
2301 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "GSM-HR-08/8000/1"));
2302 flow[1].em.portnr := 20000;
2303 f_flow_create(RTPEM[1], ep, call_id, "sendonly", flow[1]);
2304
2305
2306 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2307 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2308 f_sleep(1.0);
2309 f_flow_delete(RTPEM[0]);
2310 f_flow_delete(RTPEM[1], ep, call_id);
2311
2312 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2313 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2314
2315 /* The "Talker" will receive his RTP, so TX must match RX.
2316 * As RTP from "Listener" is ignored, no extra packets shall be received. */
2317 if (stats[0].num_pkts_tx != stats[0].num_pkts_rx) {
2318 setverdict(fail, "Talker does not receive as many packets as it transmits!");
2319 }
2320 if (stats[0].bytes_payload_tx != stats[0].bytes_payload_rx) {
2321 setverdict(fail, "Talker does not receive as many payload as it transmits!");
2322 }
2323
2324 /* The "Listener" will also receive RTP of the "Talker",
2325 * so TX of "Talker" must match RX of "Listener". */
2326 if (stats[0].num_pkts_tx != stats[1].num_pkts_rx) {
2327 setverdict(fail, "Listener does not receive as many packets as talker transmits!");
2328 }
2329 if (stats[0].bytes_payload_tx != stats[1].bytes_payload_rx) {
2330 setverdict(fail, "Listener does not receive as many payload as talker transmits!");
2331 }
2332
2333 f_rtpem_stats_err_check(stats[0]);
2334 f_rtpem_stats_err_check(stats[1]);
2335
2336 setverdict(pass);
2337 }
2338
Philipp Maier6d4e0942019-02-21 17:35:01 +01002339
2340 /* create two local RTP emulations; create two connections on MGW EP, see if
Philipp Maier8ed48c52023-02-07 11:24:31 +01002341 * exchanged data is converted between ts101318 and rfc5993 */
2342 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 +01002343 var RtpFlowData flow[2];
2344 var RtpemStats stats[2];
2345 var MgcpResponse resp;
2346 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2347 var MgcpCallId call_id := '1226'H;
2348
2349 f_init(ep);
2350
2351 /* Turn on conversion mode */
2352 f_vty_enter_config(MGWVTY);
2353 f_vty_transceive(MGWVTY, "mgcp");
2354 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2355
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002356 /* Connection #0 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002357 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000", fmtp0));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002358 /* bind local RTP emulation sockets */
2359 flow[0].em.portnr := 10000;
2360 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002361 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2362 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2363 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0;
2364 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002365 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2366
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002367 /* Connection #1 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002368 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000", fmtp1));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002369 flow[1].em.portnr := 20000;
2370 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002371 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2372 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2373 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := pl1;
2374 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := pl1;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002375 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2376
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002377 /* Send RTP packets to connection #0, receive on connection #1 */
2378 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2379 f_sleep(0.5);
2380 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002381 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002382 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2383 f_sleep(0.5);
2384 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002385
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002386 /* Send RTP packets to connection #1, receive on connection #0 */
2387 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2388 f_sleep(0.5);
2389 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2390 f_sleep(1.0);
2391 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2392 f_sleep(0.5);
2393 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2394
2395 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002396 f_flow_delete(RTPEM[0]);
2397 f_flow_delete(RTPEM[1], ep, call_id);
2398
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002399 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002400 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2401 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002402 f_rtpem_stats_err_check(stats[0]);
2403 f_rtpem_stats_err_check(stats[1]);
2404
2405 /* Turn off conversion mode */
2406 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2407
2408 setverdict(pass);
2409 }
2410
Philipp Maier8ed48c52023-02-07 11:24:31 +01002411 const octetstring rtp_hr_gsm_ts101318 := '0b11b3eede60be4e3ec68838c7b5'O;
2412 const octetstring rtp_hr_gsm_rfc5993 := '000b11b3eede60be4e3ec68838c7b5'O;
2413
2414 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2415 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "", "");
2416 }
2417
2418 testcase TC_ts101318_rfc5993_rtp_conversion_fmtp() runs on dummy_CT {
2419 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "gsm-hr-format=ts101318", "gsm-hr-format=rfc5993");
2420 }
2421
Philipp Maier4f764ce2019-03-07 10:54:10 +01002422 /* create two local RTP emulations; create two connections on MGW EP, see if
Philipp Maiereb5e8132023-04-13 16:20:37 +02002423 * exchanged data is converted between AMR octet-aligned and bandwidth
Philipp Maier4f764ce2019-03-07 10:54:10 +01002424 * efficient-mode */
Philipp Maier71484282023-04-04 18:00:53 +02002425 function f_TC_amr_x_x_rtp_conversion(charstring fmtp0, octetstring pl0,
2426 charstring fmtp1a, octetstring pl1a,
2427 charstring fmtp1b, octetstring pl1b) runs on dummy_CT {
Philipp Maier4f764ce2019-03-07 10:54:10 +01002428 var RtpFlowData flow[2];
2429 var RtpemStats stats[2];
2430 var MgcpResponse resp;
2431 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2432 var MgcpCallId call_id := '1226'H;
2433
2434 f_init(ep);
2435
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002436 /* Connection #0 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002437 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 96, "AMR/8000", fmtp0));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002438 /* bind local RTP emulation sockets */
2439 flow[0].em.portnr := 10000;
2440 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002441 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2442 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2443 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0;
2444 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0;
Philipp Maier4f764ce2019-03-07 10:54:10 +01002445 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2446
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002447 /* Connection #1 (Bidirectional) */
Philipp Maier71484282023-04-04 18:00:53 +02002448 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000", fmtp1a));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002449 flow[1].em.portnr := 20000;
2450 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maier71484282023-04-04 18:00:53 +02002451 flow[1].rtp_cfg.rx_payloads := {};
2452 flow[1].rtp_cfg.tx_payloads := {};
2453 if (pl1a != ''O) {
2454 flow[1].rtp_cfg.rx_payloads := flow[1].rtp_cfg.rx_payloads & {{112, pl1a}};
2455 flow[1].rtp_cfg.tx_payloads := flow[1].rtp_cfg.tx_payloads & {{112, pl1a}};
2456 }
2457
2458 /* The second fmtp parameter is to simulate a call agent that offers the transmission both modes. */
2459 if (fmtp1b != "") {
2460 flow[1].codec_descr := flow[1].codec_descr & {{113, "AMR/8000", fmtp1b}};
2461 if (pl1b != ''O) {
2462 flow[1].rtp_cfg.rx_payloads := flow[1].rtp_cfg.rx_payloads & {{113, pl1b}};
2463 flow[1].rtp_cfg.tx_payloads := flow[1].rtp_cfg.tx_payloads & {{113, pl1b}};
2464 }
2465 }
2466
Philipp Maier4f764ce2019-03-07 10:54:10 +01002467 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2468
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002469 /* Send RTP packets to connection #0, receive on connection #1 */
2470 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2471 f_sleep(0.5);
2472 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002473 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002474 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2475 f_sleep(0.5);
2476 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002477
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002478 /* Send RTP packets to connection #1, receive on connection #0 */
2479 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2480 f_sleep(0.5);
2481 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2482 f_sleep(1.0);
2483 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2484 f_sleep(0.5);
2485 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2486
2487 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002488 f_flow_delete(RTPEM[0]);
2489 f_flow_delete(RTPEM[1], ep, call_id);
2490
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002491 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002492 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2493 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002494 f_rtpem_stats_err_check(stats[0]);
2495 f_rtpem_stats_err_check(stats[1]);
2496
2497 setverdict(pass);
2498 }
2499
Philipp Maier882843d2020-05-25 15:33:13 +02002500 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2501 * functions are real world AMR RTP payloads including AMR header. The
2502 * payloads were extracted from a trace with known good payloads. */
2503
Philipp Maiercd756ed2023-02-13 11:12:24 +01002504 const octetstring rtp_amr_5_90k_oa := '2014e959f35fdfe5e9667ffbc088818088'O;
2505 const octetstring rtp_amr_5_90k_bwe := '217a567cd7f7f97a599ffef022206022'O;
2506 const octetstring rtp_amr_5_15k_oa := '100c4e9ba850e30d5d53d04de41e7c'O;
2507 const octetstring rtp_amr_5_15k_bwe := '10d3a6ea1438c35754f41379079f'O;
2508
Philipp Maier71484282023-04-04 18:00:53 +02002509 /* Only one codec on each side */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002510 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier71484282023-04-04 18:00:53 +02002511 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_90k_oa, "octet-align=0", rtp_amr_5_90k_bwe, "", ''O);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002512 }
Philipp Maier4f764ce2019-03-07 10:54:10 +01002513 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
Philipp Maier71484282023-04-04 18:00:53 +02002514 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa, "octet-align=1", rtp_amr_5_15k_oa, "", ''O);
2515 }
2516 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2517 f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe, "octet-align=0", rtp_amr_5_15k_bwe, "", ''O);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002518 }
2519
Philipp Maier71484282023-04-04 18:00:53 +02002520 /* Only one codec on one side, two codecs (compatibility) on other side. The payloads are the same on both
2521 * sides, so the expectation is that conversion must not be performed. Each test is done with both formats in
2522 * two different configurations.*/
2523 testcase TC_amr_oa_oa_no_bwe_rtp_conversion() runs on dummy_CT {
2524 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa,
2525 "octet-align=1", rtp_amr_5_15k_oa,
2526 "octet-align=0", ''O); /* We expect to see NO bandwidth efficient packets! */
2527 }
2528 testcase TC_amr_oa_no_bwe_oa_rtp_conversion() runs on dummy_CT {
2529 /* (Same as above but flipped on the opposite side) */
2530 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa,
2531 "octet-align=0", ''O, /* We expect to see NO bandwidth efficient packets! */
2532 "octet-align=1", rtp_amr_5_15k_oa);
2533 }
2534 testcase TC_amr_bwe_bwe_no_oa_rtp_conversion() runs on dummy_CT {
2535 f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe,
2536 "octet-align=0", rtp_amr_5_15k_bwe,
2537 "octet-align=1", ''O); /* We expect to see NO octet aligned packets! */
2538 }
2539 testcase TC_amr_bwe_no_oa_bwe_rtp_conversion() runs on dummy_CT {
2540 /* (Same as above but flipped on the opposite side) */
2541 f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe,
2542 "octet-align=1", ''O, /* We expect to see NO octet aligned packets! */
2543 "octet-align=0", rtp_amr_5_15k_bwe);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002544 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002545
Harald Weltee636afd2017-09-17 16:24:09 +08002546 /* TODO: Double-DLCX (no retransmission) */
2547
2548
2549
2550 /* TODO: AUEP (various) */
2551 /* TODO: RSIP (various) */
2552 /* TODO: RQNT (various) */
2553 /* TODO: EPCF (various) */
2554 /* TODO: AUCX (various) */
2555 /* TODO: invalid verb (various) */
2556
Oliver Smith021141e2019-06-25 12:09:01 +02002557
2558 testcase TC_conn_timeout() runs on dummy_CT {
2559 var RtpFlowData flow;
2560 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2561 var MgcpCallId call_id := '1225'H;
2562 var MGCP_RecvFrom mrf;
2563
2564 f_init(ep);
2565 log("Setting conn-timeout to 1s");
Pau Espin Pedrol30da2392023-09-27 16:49:20 +02002566 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1");
Oliver Smith021141e2019-06-25 12:09:01 +02002567
2568 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002569 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002570 flow.em.portnr := 10000;
2571 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2572 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2573 f_sleep(1.5);
2574
2575 log("Stopping for 0.5s and resuming");
2576 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2577 f_sleep(0.5);
2578 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2579 f_sleep(0.1);
2580
2581 log("Stopping for 1.5s, expecting to run into timeout");
2582 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2583 f_sleep(1.5);
2584
2585 log("Resuming should fail now");
2586 f_rtpem_conn_refuse_expect(RTPEM[0]);
2587 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2588 f_sleep(0.2);
2589 f_rtpem_conn_refuse_verify(RTPEM[0]);
2590
Pau Espin Pedrol30da2392023-09-27 16:49:20 +02002591 log("Setting conn-timeout back to 0 (disabled)");
2592 f_vty_config(MGWVTY, "mgcp", "conn-timeout 0");
2593
Oliver Smith021141e2019-06-25 12:09:01 +02002594 setverdict(pass);
2595 }
2596
Philipp Maier2609c752020-07-08 12:38:09 +02002597 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2598 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2599 var template MgcpCommand cmd;
2600 var MgcpResponse resp;
2601 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2602 var MgcpCallId call_id := '8376F297'H;
2603
2604 f_init(ep);
2605
2606 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2607 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2608
2609 f_dlcx_ok(ep);
2610
2611 setverdict(pass);
2612 }
2613
2614 /* Test what happens when overlapping endpoints are selected (E1) */
2615 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2616 var template MgcpCommand cmd;
2617 var MgcpResponse resp;
2618 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2619 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2620 var MgcpCallId call_id_1 := '8376F297'H;
2621 var MgcpCallId call_id_2 := '837AF2A7'H;
2622
2623 f_init();
2624
2625 /* ep_1 and ep_2 are overlapping, selecting both one after
2626 * another should work fine: */
2627 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2628 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2629 f_dlcx_ok(ep_1);
2630 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2631 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2632 f_dlcx_ok(ep_2);
2633
2634 /* When ep_1 is serving a call we can not select ep_2 becaus
2635 * it is overlapping with ep_1 */
2636 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2637 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2638 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2639 resp := mgcp_transceive_mgw(cmd, ?);
2640 if (resp.line.code != "501") {
2641 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2642 }
2643 f_dlcx_ok(ep_1);
2644
2645 setverdict(pass);
2646 }
2647
2648 /* Create one connection in loopback mode, test if the RTP packets are
2649 * actually reflected */
2650 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2651 var RtpFlowData flow;
2652 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2653 var MgcpCallId call_id := '12250989'H;
2654 var RtpemStats stats;
2655
2656 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002657 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002658 flow.em.portnr := 10000;
2659 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2660
2661 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2662 f_sleep(1.0);
2663 f_flow_delete(RTPEM[0], ep, call_id);
2664
2665 stats := f_rtpem_stats_get(RTPEM[0]);
2666
2667 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2668 setverdict(fail);
2669 }
2670 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2671 setverdict(fail);
2672 }
2673
2674 f_rtpem_stats_err_check(stats);
2675
2676 setverdict(pass);
2677 }
2678
Philipp Maier13aff992022-06-30 16:19:59 +02002679 /* test valid wildcarded DLCX on an E1 trunk */
2680 testcase TC_e1_dlcx_wildcarded() runs on dummy_CT {
2681 var template MgcpCommand cmd;
2682 var MgcpEndpoint ep;
2683 var MgcpCallId call_id := '8376F297'H;
2684 var integer n_e1_ts := 4;
2685 var StatsDExpects expect;
2686
2687 f_init();
2688
2689 /* Open a few E1 timeslots */
2690 for (var integer i := 0; i < n_e1_ts; i := i+1) {
2691 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-0@" & c_mgw_domain;
2692 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2693 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2694 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-2@" & c_mgw_domain;
2695 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2696 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2697 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-4@" & c_mgw_domain;
2698 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2699 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2700 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-6@" & c_mgw_domain;
2701 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2702 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2703 }
2704
2705 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
2706 * occupied endpoints */
2707 f_sleep(1.0)
2708 expect := {
2709 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := n_e1_ts * 4, max := n_e1_ts * 4}
2710 };
2711 f_statsd_expect(expect);
2712
2713 /* Send wildcarded DLCX */
2714 var template MgcpResponse rtmpl := {
2715 line := {
2716 code := "200",
2717 string := ?
2718 },
2719 params:= { },
2720 sdp := omit
2721 };
2722 ep := "ds/e1-1/*@" & c_mgw_domain;
2723 cmd := ts_DLCX(get_next_trans_id(), ep);
2724 mgcp_transceive_mgw(cmd, rtmpl);
2725
2726 /* Query a the statsd once to ensure that intermediate results are pulled from the
2727 * pipeline. The second query (below) will return the actual result. */
2728 expect := {
2729 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := n_e1_ts * 4}
2730 };
2731 f_statsd_expect(expect);
2732
2733 /* The second query must resturn a result with 0 endpoints in use. */
2734 expect := {
2735 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := 0}
2736 };
2737 f_statsd_expect(expect);
2738
2739 setverdict(pass);
2740 }
2741
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002742 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2743 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2744 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2745 var template MgcpCommand cmd;
2746 var MgcpResponse resp;
2747 var MgcpCallId call_id := '1234'H;
2748 var MgcpConnectionId conn_id;
2749
2750 f_init(ep);
2751
2752 /* create the connection on the MGW */
2753 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2754 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2755 conn_id := extract_conn_id(resp);
2756
2757 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2758 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2759 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2760 valueof(ts_SDP_ptime(20)) });
2761 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2762
2763 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2764 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2765 }
2766 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2767 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2768 }
2769
2770 /* clean-up */
2771 f_dlcx_ok(ep, call_id);
2772 setverdict(pass);
2773 }
2774
2775 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2776 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2777 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2778 var template MgcpCommand cmd;
2779 var MgcpResponse resp;
2780 var MgcpCallId call_id := '1234'H;
2781 var MgcpConnectionId conn_id;
2782
2783 f_init(ep);
2784
2785 /* create the connection on the MGW */
2786 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2787 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2788 conn_id := extract_conn_id(resp);
2789
2790 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2791 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2792 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2793 valueof(ts_SDP_ptime(20)) });
2794 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2795
2796 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2797 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2798 }
2799 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2800 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2801 }
2802
2803 /* clean-up */
2804 f_dlcx_ok(ep, call_id);
2805 setverdict(pass);
2806 }
2807
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002808 /* create two local emulations and pass data in both directions */
2809 function f_two_crcx_mdcx_data_transfer(MgcpEndpoint ep, MgcpCallId call_id, inout RtpFlowData flow_a,
2810 inout RtpFlowData flow_b, boolean tear_down_rtp := true) runs on dummy_CT {
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002811 var RtpemStats stats[2];
2812 var MgcpResponse resp;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002813 var integer num_pkts_tx[2];
2814 var integer temp;
2815
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002816 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002817 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow_a, true);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002818 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2819
2820 /* Create the second connection. This connection will be also
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002821 * in receive only mode (CN side, regular RTP) */
2822 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow_b, true);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002823 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2824
2825 /* The first leg starts transmitting */
2826 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2827 f_sleep(0.5);
2828 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2829 if (stats[0].num_pkts_rx_err_disabled != 0) {
2830 setverdict(fail, "received packets from MGW on recvonly connection 0");
2831 mtc.stop;
2832 }
2833 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2834 if (stats[1].num_pkts_rx_err_disabled != 0) {
2835 setverdict(fail, "received packets from MGW on recvonly connection 1");
2836 mtc.stop;
2837 }
2838
2839 /* The second leg starts transmitting a little later */
2840 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2841 f_sleep(1.0);
2842 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2843 if (stats[0].num_pkts_rx_err_disabled != 0) {
2844 setverdict(fail, "received packets from MGW on recvonly connection 0");
2845 mtc.stop;
2846 }
2847 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2848 if (stats[1].num_pkts_rx_err_disabled != 0) {
2849 setverdict(fail, "received packets from MGW on recvonly connection 1");
2850 mtc.stop;
2851 }
2852
2853 /* The first leg will now be switched into bidirectional
2854 * mode, but we do not expect any data coming back yet. */
2855 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2856 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2857 num_pkts_tx[1] := stats[1].num_pkts_tx;
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002858 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow_a);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002859 f_sleep(0.5);
2860 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2861 if (stats[0].num_pkts_rx_err_disabled != 0) {
2862 setverdict(fail, "received packets from MGW on recvonly connection 0");
2863 mtc.stop;
2864 }
2865 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2866 if (stats[1].num_pkts_rx_err_disabled != 0) {
2867 setverdict(fail, "received packets from MGW on recvonly connection 1");
2868 mtc.stop;
2869 }
2870
2871 /* When the second leg is switched into bidirectional mode
2872 * as well, then the MGW will connect the two together and
2873 * we should see RTP streams passing through from both ends. */
2874 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2875 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2876 num_pkts_tx[0] := stats[0].num_pkts_tx;
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002877 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow_b);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002878 f_sleep(2.0);
2879
2880 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2881 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2882
2883 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2884 if (temp > 3 or temp < -3) {
2885 setverdict(fail, "number of packets not within normal parameters:", temp);
2886 mtc.stop;
2887 }
2888
2889 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2890 if (temp > 3 or temp < -3) {
2891 setverdict(fail, "number of packets not within normal parameters:", temp);
2892 mtc.stop;
2893 }
2894
2895 f_rtpem_stats_err_check(stats[0]);
2896 f_rtpem_stats_err_check(stats[1]);
2897
2898 /* Tear down */
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002899 if (tear_down_rtp) {
2900 f_flow_delete(RTPEM[0]);
2901 f_flow_delete(RTPEM[1], ep, call_id);
2902 }
2903 setverdict(pass);
2904 }
2905
2906 /* create two local RTP+IuUP emulations and pass data in both directions */
2907 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
2908 IuUP_RabFlowCombinationList rfcl_a,
2909 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2910 var RtpFlowData flow[2];
2911 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2912 var MgcpCallId call_id := '1227'H;
2913
2914 f_init(ep);
2915
2916 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2917 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2918 flow[0].em.portnr := 10000;
2919 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2920 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2921 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2922 flow[0].rtp_cfg.iuup_mode := true;
2923 flow[0].rtp_cfg.iuup_cfg.active_init := true;
2924 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
2925
2926 /* Create the second connection. This connection will be also
2927 * in receive only mode (CN side, IuUP-Init passive) */
2928 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2929 flow[1].em.portnr := 20000;
2930 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2931 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2932 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2933 flow[1].rtp_cfg.iuup_mode := true;
2934 flow[1].rtp_cfg.iuup_cfg.active_init := false;
2935
2936 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], true);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002937 setverdict(pass);
2938 }
2939 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002940 var template (value) IuUP_RabFlowCombinationList rfcl := {
2941 t_IuUP_RFC_AMR_12_2(0),
2942 t_IuUP_RFC_AMR_SID(1),
2943 t_IuUP_RFC_AMR_NO_DATA(2)
2944 };
2945 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2946 mp_local_ipv4, mp_remote_ipv4);
2947 }
2948 /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */
2949 testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT {
2950 var template (value) IuUP_RabFlowCombinationList rfcl := {
2951 t_IuUP_RFC_AMR_12_2(1),
2952 t_IuUP_RFC_AMR_SID(2),
2953 t_IuUP_RFC_AMR_NO_DATA(0)
2954 };
2955 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002956 mp_local_ipv4, mp_remote_ipv4);
2957 }
2958
Pau Espin Pedrole6d095c2023-09-27 14:47:45 +02002959 /* Test that once IuUP->RTP has been set up, if the RTP/IuUP conn is set
2960 * as "recvonly", no more RTP/IuUP packets get out of the MGW. */
2961 function f_tc_two_crcx_mdcx_and_iuup_mdcx_recvonly(charstring local_ip_a, charstring remote_ip_a,
2962 IuUP_RabFlowCombinationList rfcl_a,
2963 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2964 var RtpFlowData flow[2];
2965 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2966 var MgcpCallId call_id := '1227'H;
2967 var RtpemStats stats[2];
2968
2969 f_init(ep);
2970
2971 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2972 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2973 flow[0].em.portnr := 10000;
2974 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2975 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2976 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2977 flow[0].rtp_cfg.iuup_mode := true;
2978 flow[0].rtp_cfg.iuup_cfg.active_init := true;
2979 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
2980
2981 /* Create the second connection. This connection will be also
2982 * in receive only mode (CN side, IuUP-Init passive) */
2983 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2984 flow[1].em.portnr := 20000;
2985 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2986 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2987 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2988 flow[1].rtp_cfg.iuup_mode := true;
2989 flow[1].rtp_cfg.iuup_cfg.active_init := false;
2990
2991 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], false);
2992
2993 /* Now validate we don't receive more RTP packets after setting it to recvonly: */
2994 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2995 f_flow_modify(RTPEM[1], ep, call_id, "recvonly", flow[1]);
2996 f_sleep(0.5);
2997 stats[0] := f_rtpem_stats_get(RTPEM[1]);
2998 f_sleep(0.5);
2999 stats[1] := f_rtpem_stats_get(RTPEM[1]);
3000 if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) {
3001 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3002 log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " RTP packets from MGW on recvonly connection 1"));
3003 }
3004
3005 /* Now do the same on the IuUP port: */
3006 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
3007 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
3008 f_flow_modify(RTPEM[0], ep, call_id, "recvonly", flow[0]);
3009 f_sleep(0.5);
3010 stats[0] := f_rtpem_stats_get(RTPEM[0]);
3011 f_sleep(0.5);
3012 stats[1] := f_rtpem_stats_get(RTPEM[0]);
3013 if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) {
3014 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3015 log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " IuUP packets from MGW on recvonly connection 1"));
3016 }
3017
3018 /* Tear down */
3019 f_flow_delete(RTPEM[0]);
3020 f_flow_delete(RTPEM[1], ep, call_id);
3021 setverdict(pass);
3022 }
3023 testcase TC_two_crcx_mdcx_and_iuup_mdcx_recvonly() runs on dummy_CT {
3024 var template (value) IuUP_RabFlowCombinationList rfcl := {
3025 t_IuUP_RFC_AMR_12_2(0),
3026 t_IuUP_RFC_AMR_SID(1),
3027 t_IuUP_RFC_AMR_NO_DATA(2)
3028 };
3029 f_tc_two_crcx_mdcx_and_iuup_mdcx_recvonly(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
3030 mp_local_ipv4, mp_remote_ipv4);
3031 }
3032
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003033 /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */
3034 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 +02003035 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003036 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
3037 var RtpFlowData flow[2];
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003038 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
3039 var MgcpCallId call_id := '1227'H;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003040
3041 f_init(ep);
3042
3043 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
3044 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
3045 flow[0].em.portnr := 10000;
3046 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02003047 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3048 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3049 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
3050 /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
3051 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003052 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02003053 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003054 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003055
3056 /* Create the second connection. This connection will be also
3057 * in receive only mode (CN side, regular RTP) */
3058 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
3059 flow[1].em.portnr := 20000;
3060 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02003061 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3062 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3063 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
3064 /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
3065 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003066 flow[1].rtp_cfg.iuup_mode := false;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003067
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02003068 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], true);
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003069 setverdict(pass);
3070 }
3071 testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003072 var template (value) IuUP_RabFlowCombinationList rfcl := {
3073 t_IuUP_RFC_AMR_12_2(0),
3074 t_IuUP_RFC_AMR_SID(1),
3075 t_IuUP_RFC_AMR_NO_DATA(2)
3076 };
3077 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
3078 mp_local_ipv4, mp_remote_ipv4);
3079 }
3080 /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */
3081 testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT {
3082 var template (value) IuUP_RabFlowCombinationList rfcl := {
3083 t_IuUP_RFC_AMR_12_2(1),
3084 t_IuUP_RFC_AMR_SID(2),
3085 t_IuUP_RFC_AMR_NO_DATA(0)
3086 };
3087 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003088 mp_local_ipv4, mp_remote_ipv4);
3089 }
3090
Pau Espin Pedrole6d095c2023-09-27 14:47:45 +02003091 /* Test that once IuUP->RTP has been set up, if the RTP/IuUP conn is set
3092 * as "recvonly", no more RTP/IuUP packets get out of the MGW. */
3093 function f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly(charstring local_ip_a, charstring remote_ip_a,
3094 IuUP_RabFlowCombinationList rfcl_a,
3095 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
3096 var RtpFlowData flow[2];
3097 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
3098 var MgcpCallId call_id := '1227'H;
3099 var RtpemStats stats[2];
3100
3101 f_init(ep);
3102
3103 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
3104 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
3105 flow[0].em.portnr := 10000;
3106 flow[0].rtp_cfg := c_RtpemDefaultCfg;
3107 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3108 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3109 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
3110 /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
3111 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O;
3112 flow[0].rtp_cfg.iuup_mode := true;
3113 flow[0].rtp_cfg.iuup_cfg.active_init := true;
3114 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
3115
3116 /* Create the second connection. This connection will be also
3117 * in receive only mode (CN side, regular RTP) */
3118 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
3119 flow[1].em.portnr := 20000;
3120 flow[1].rtp_cfg := c_RtpemDefaultCfg;
3121 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3122 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3123 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
3124 /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
3125 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O;
3126 flow[1].rtp_cfg.iuup_mode := false;
3127
3128 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], false);
3129
3130 /* Now validate we don't receive more RTP packets after setting it to recvonly: */
3131 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
3132 f_flow_modify(RTPEM[1], ep, call_id, "recvonly", flow[1]);
3133 f_sleep(0.5);
3134 stats[0] := f_rtpem_stats_get(RTPEM[1]);
3135 f_sleep(0.5);
3136 stats[1] := f_rtpem_stats_get(RTPEM[1]);
3137 if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) {
3138 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3139 log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " RTP packets from MGW on recvonly connection 1"));
3140 }
3141
3142 /* Now do the same on the IuUP port: */
3143 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
3144 f_flow_modify(RTPEM[0], ep, call_id, "recvonly", flow[0]);
3145 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
3146 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
3147 f_sleep(0.5);
3148 stats[0] := f_rtpem_stats_get(RTPEM[0]);
3149 f_sleep(0.5);
3150 stats[1] := f_rtpem_stats_get(RTPEM[0]);
3151 if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) {
3152 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3153 log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " IuUP packets from MGW on recvonly connection 1"));
3154 }
3155
3156 /* Tear down */
3157 f_flow_delete(RTPEM[0]);
3158 f_flow_delete(RTPEM[1], ep, call_id);
3159 setverdict(pass);
3160 }
3161 testcase TC_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly() runs on dummy_CT {
3162 var template (value) IuUP_RabFlowCombinationList rfcl := {
3163 t_IuUP_RFC_AMR_12_2(0),
3164 t_IuUP_RFC_AMR_SID(1),
3165 t_IuUP_RFC_AMR_NO_DATA(2)
3166 };
3167 f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
3168 mp_local_ipv4, mp_remote_ipv4);
3169 }
3170
Pau Espin Pedrol0bc02122023-09-27 14:48:10 +02003171 /* Set up Endpoint with 1 IuUP conn and 1 RTP-AMR conn, then MDCX the later to become IuUP. */
3172 function f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup(charstring local_ip_a, charstring remote_ip_a,
3173 IuUP_RabFlowCombinationList rfcl_a,
3174 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
3175 var RtpFlowData flow[2];
3176 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
3177 var MgcpCallId call_id := '1227'H;
3178 var RtpemStats stats[2][2];
3179
3180 f_init(ep);
3181
3182 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
3183 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
3184 flow[0].em.portnr := 10000;
3185 flow[0].rtp_cfg := c_RtpemDefaultCfg;
3186 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3187 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3188 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
3189 /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
3190 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O;
3191 flow[0].rtp_cfg.iuup_mode := true;
3192 flow[0].rtp_cfg.iuup_cfg.active_init := true;
3193 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
3194
3195 /* Create the second connection. This connection will be also
3196 * in receive only mode (CN side, regular RTP) */
3197 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
3198 flow[1].em.portnr := 20000;
3199 flow[1].rtp_cfg := c_RtpemDefaultCfg;
3200 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3201 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3202 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
3203 /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
3204 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O;
3205 flow[1].rtp_cfg.iuup_mode := false;
3206
3207 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], false);
3208 setverdict(pass);
3209
3210 /* Stop sending further pkts to RTPEM[1], to avoid race conditions where RTP is sent while we
3211 * reconfigure MGW (and our RTPEM) for IuUP:
3212 */
3213 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
3214 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
3215 f_sleep(0.5);
3216
3217 /* Modify the AMR side to also do IuUP */
3218 flow[1].codec_descr := {{
3219 pt := 96,
3220 codec := "VND.3GPP.IUFP/16000",
3221 fmtp := omit
3222 }};
3223 flow[1].rtp_cfg.iuup_mode := true;
3224 flow[1].rtp_cfg.iuup_cfg.active_init := false;
3225 flow[1].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
3226 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3227 /* Send whatever payload is expected by the other RTPEM, now without AMR-BE_RTP<->AMR-IUUP conversion: */
3228 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := flow[0].rtp_cfg.rx_payloads[0].fixed_payload;
3229 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3230 /* Expect whatever payload is sent by the other RTPEM, now without AMR-BE_RTP<->AMR-IUUP conversion: */
3231 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := flow[0].rtp_cfg.tx_payloads[0].fixed_payload;
3232 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
3233 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
3234 f_sleep(1.0);
3235 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
3236
3237 /* Now, if MGW would keep sending RTP instead of expected IuUP, then that would make the RTPEM[1]
3238 * component to fail, since it would error trying to decode an RTP pkt as IuUP.
3239 * We simply need to make sure some packets are being forwarded to it:
3240 */
3241 stats[0][0] := f_rtpem_stats_get(RTPEM[0]);
3242 stats[1][0] := f_rtpem_stats_get(RTPEM[1]);
3243 f_sleep(1.0);
3244 stats[0][1] := f_rtpem_stats_get(RTPEM[0]);
3245 stats[1][1] := f_rtpem_stats_get(RTPEM[1]);
3246
3247 f_rtpem_stats_err_check(stats[0][1]);
3248 f_rtpem_stats_err_check(stats[1][1]);
3249 if (stats[0][1].num_pkts_rx == stats[0][0].num_pkts_rx) {
3250 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3251 log2str("No packets received in connection 0 after MDCX"));
3252 }
3253 if (stats[1][1].num_pkts_rx == stats[1][0].num_pkts_rx) {
3254 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3255 log2str("No packets received in connection 1 after MDCX"));
3256 }
3257
3258 /* Tear down */
3259 f_flow_delete(RTPEM[0]);
3260 f_flow_delete(RTPEM[1], ep, call_id);
3261 setverdict(pass);
3262 }
3263 testcase TC_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup() runs on dummy_CT {
3264 var template (value) IuUP_RabFlowCombinationList rfcl := {
3265 t_IuUP_RFC_AMR_12_2(0),
3266 t_IuUP_RFC_AMR_SID(1),
3267 t_IuUP_RFC_AMR_NO_DATA(2)
3268 };
3269 f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
3270 mp_local_ipv4, mp_remote_ipv4);
3271 }
Harald Welte00a067f2017-09-13 23:27:17 +02003272 control {
3273 execute(TC_selftest());
Pau Espin Pedrolb26d4462023-06-15 11:38:02 +02003274 execute(TC_auep_null());
Harald Welte3c6ebb92017-09-16 00:56:57 +08003275 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02003276 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01003277 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08003278 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02003279 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08003280 execute(TC_crcx_early_bidir_mode());
3281 execute(TC_crcx_unsupp_param());
3282 execute(TC_crcx_missing_callid());
3283 execute(TC_crcx_missing_mode());
3284 execute(TC_crcx_unsupp_packet_intv());
3285 execute(TC_crcx_illegal_double_lco());
3286 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01003287 execute(TC_crcx_wildcarded());
3288 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08003289 execute(TC_mdcx_without_crcx());
3290 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02003291 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01003292 execute(TC_mdcx_wildcarded());
3293 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01003294 execute(TC_crcx_and_dlcx_ep_callid_connid());
3295 execute(TC_crcx_and_dlcx_ep_callid());
3296 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01003297 execute(TC_crcx_and_dlcx_ep_callid_inval());
3298 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01003299 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01003300
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003301 execute(TC_crcx_osmux_wildcard());
3302 execute(TC_crcx_osmux_fixed());
3303 execute(TC_crcx_osmux_fixed_twice());
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02003304 execute(TC_crcx_osmux_257());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003305 execute(TC_one_crcx_receive_only_osmux());
3306 execute(TC_one_crcx_loopback_osmux());
3307 execute(TC_two_crcx_and_rtp_osmux());
3308 execute(TC_two_crcx_and_rtp_osmux_bidir());
Pau Espin Pedrol26258472022-10-25 12:51:24 +02003309 execute(TC_two_crcx_and_rtp_osmux_bidir_amr_bwe());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003310 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
3311 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
3312
Harald Welte33d82162017-12-28 03:21:57 +01003313 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02003314
3315 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02003316
3317 execute(TC_one_crcx_receive_only_rtp());
3318 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02003319 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02003320 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02003321 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02003322 execute(TC_two_crcx_diff_pt_and_rtp());
3323 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02003324 execute(TC_two_crcx_mdcx_and_rtp());
3325 execute(TC_two_crcx_and_unsolicited_rtp());
3326 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Andreas Eversbergdb99cb02023-07-12 11:44:11 +02003327 execute(TC_two_crcx_confecho_sendonly_rtp());
Philipp Maier6d4e0942019-02-21 17:35:01 +01003328 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier8ed48c52023-02-07 11:24:31 +01003329 execute(TC_ts101318_rfc5993_rtp_conversion_fmtp());
Philipp Maier4f764ce2019-03-07 10:54:10 +01003330 execute(TC_amr_oa_bwe_rtp_conversion());
3331 execute(TC_amr_oa_oa_rtp_conversion());
3332 execute(TC_amr_bwe_bwe_rtp_conversion());
Philipp Maier71484282023-04-04 18:00:53 +02003333 execute(TC_amr_oa_oa_no_bwe_rtp_conversion());
3334 execute(TC_amr_oa_no_bwe_oa_rtp_conversion());
3335 execute(TC_amr_bwe_bwe_no_oa_rtp_conversion());
3336 execute(TC_amr_bwe_no_oa_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02003337
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01003338 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02003339
3340 execute(TC_e1_crcx_and_dlcx_ep());
3341 execute(TC_e1_crcx_with_overlap());
3342 execute(TC_e1_crcx_loopback());
Philipp Maier13aff992022-06-30 16:19:59 +02003343 execute(TC_e1_dlcx_wildcarded());
Philipp Maier2609c752020-07-08 12:38:09 +02003344
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02003345 execute(TC_crcx_mdcx_ip4());
3346 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02003347 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
3348 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
3349 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
3350 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
3351 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
3352 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
3353 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
3354 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02003355
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003356 execute(TC_two_crcx_mdcx_and_iuup());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003357 execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered());
Pau Espin Pedrole6d095c2023-09-27 14:47:45 +02003358 execute(TC_two_crcx_mdcx_and_iuup_mdcx_recvonly());
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003359 execute(TC_two_crcx_mdcx_and_iuup_rtp());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003360 execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered());
Pau Espin Pedrole6d095c2023-09-27 14:47:45 +02003361 execute(TC_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly());
Pau Espin Pedrol0bc02122023-09-27 14:48:10 +02003362 execute(TC_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup());
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003363
Oliver Smith871f45a2023-01-24 16:21:50 +01003364 execute(TC_two_crcx_mdcx_and_rtp_clearmode());
3365
Philipp Maier37965082021-05-25 16:44:25 +02003366 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
3367 * older versions of osmo-mgw. This eventually leads into
3368 * a failure of all subsequent testcases, so it is important
3369 * not to add new testcaes after this one. */
3370 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02003371 }
3372}