blob: 7090b34041079780f15fd6b71af7b9f300393660 [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;
Pau Espin Pedrol1158cc62024-03-21 17:21:35 +010018 import from SDP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080019 import from MGCP_CodecPort all;
20 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +010021 import from RTP_CodecPort all;
22 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020023 import from RTP_Emulation all;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +020024 import from IuUP_Emulation all;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020025 import from OSMUX_Types all;
26 import from OSMUX_CodecPort all;
27 import from OSMUX_CodecPort_CtrlFunct all;
28 import from OSMUX_Emulation all;
Pau Espin Pedrol71ed4632022-09-02 18:51:19 +020029 import from AMR_Types all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080030 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010031 import from General_Types all;
32 import from Native_Functions all;
33 import from IPCP_Types all;
34 import from IP_Types all;
35 import from Osmocom_VTY_Functions all;
36 import from TELNETasp_PortType all;
Philipp Maier55b90542021-07-02 12:33:19 +020037 import from StatsD_Types all;
38 import from StatsD_CodecPort all;
39 import from StatsD_CodecPort_CtrlFunct all;
40 import from StatsD_Checker all;
Philipp Maier3560bd62021-08-19 11:52:23 +020041 import from Osmocom_CTRL_Functions all;
42 import from Osmocom_CTRL_Types all;
43 import from Osmocom_CTRL_Adapter all;
Harald Welte00a067f2017-09-13 23:27:17 +020044
Philipp Maierbb7a01c2018-02-01 12:32:57 +010045 const charstring c_mgw_domain := "mgw";
46 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
47
Harald Welte21ba5572017-09-19 17:55:05 +080048 /* any variables declared in the component will be available to
49 * all functions that 'run on' the named component, similar to
50 * class members in C++ */
Philipp Maier3560bd62021-08-19 11:52:23 +020051 type component dummy_CT extends StatsD_ConnHdlr, CTRL_Adapter_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080052 port MGCP_CODEC_PT MGCP;
53 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010054 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080055 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010056
Philipp Maier2321ef92018-06-27 17:52:04 +020057 var RTP_Emulation_CT vc_RTPEM[3];
58 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010059
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020060 var OSMUX_Emulation_CT vc_OsmuxEM;
61 port OsmuxEM_CTRL_PT OsmuxEM;
62
Philipp Maier6137c322019-02-20 16:13:41 +010063 port TELNETasp_PT MGWVTY;
Philipp Maier55b90542021-07-02 12:33:19 +020064
65 var StatsD_Checker_CT vc_STATSD;
Harald Welte00a067f2017-09-13 23:27:17 +020066 };
67
Harald Weltee1e18c52017-09-17 16:23:07 +080068 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
69 var MgcpTransId tid := int2str(g_trans_id);
70 g_trans_id := g_trans_id + 1;
71 return tid;
72 }
73
Harald Welte21ba5572017-09-19 17:55:05 +080074 /* all parameters declared here can be modified / overridden by
75 * the config file in the [MODULE_PARAMETERS] section. If no
76 * config file is used or the file doesn't specify them, the
77 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080078 modulepar {
79 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020080 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020081 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080082 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020083 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020084 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010085 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020086 PortNumber mp_local_osmux_port := 1985;
Philipp Maier55b90542021-07-02 12:33:19 +020087 PortNumber mp_mgw_statsd_port := 8125;
Philipp Maier3560bd62021-08-19 11:52:23 +020088 PortNumber mp_mgw_ctrl_port := 4267;
Pau Espin Pedrole7928872022-10-07 10:57:11 +020089 /* Maximum number of available endpoints in osmo-mgw.cfg ("number endpoints"): */
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +020090 integer mp_num_endpoints := 300;
Harald Welte3c6ebb92017-09-16 00:56:57 +080091 }
92
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020093 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
94 /* Turn on conversion mode */
95 f_vty_enter_config(MGWVTY);
96 f_vty_transceive(MGWVTY, "mgcp");
97 if (osmux_on) {
98 f_vty_transceive(MGWVTY, "osmux on");
99 } else {
100 f_vty_transceive(MGWVTY, "osmux off");
101 }
102 f_vty_transceive(MGWVTY, "exit");
103 f_vty_transceive(MGWVTY, "exit");
104
105 }
106
107 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +0100108 map(self:MGWVTY, system:MGWVTY);
109 f_vty_set_prompts(MGWVTY);
110 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200111
112 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +0100113 }
114
Harald Weltebb7523b2018-03-29 08:52:01 +0200115 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
116 runs on dummy_CT {
117 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
118 map(comp_ref:RTP, system:RTP);
119 map(comp_ref:RTCP, system:RTCP);
120 comp_ref.start(RTP_Emulation.f_main());
121 }
122
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200123 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
124 runs on dummy_CT {
125 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
126 map(comp_ref:OSMUX, system:OSMUX);
127 comp_ref.start(OSMUX_Emulation.f_main());
128 }
129
Harald Welte21ba5572017-09-19 17:55:05 +0800130 /* initialization function, called by each test case at the
131 * beginning, but 'initialized' variable ensures its body is
132 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200133 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800134 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100135 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200136
Harald Welteedc45c12017-11-18 19:15:05 +0100137 if (initialized == false) {
138 initialized := true;
139
140 /* some random number for the initial transaction id */
141 g_trans_id := float2int(rnd()*65535.0);
142 map(self:MGCP, system:MGCP_CODEC_PT);
143 /* connect the MGCP test port using the given
144 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
145 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200146 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 +0200147 if (not ispresent(res.connId)) {
148 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200149 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200150 }
Harald Welteedc45c12017-11-18 19:15:05 +0100151 g_mgcp_conn_id := res.connId;
152
Harald Weltebb7523b2018-03-29 08:52:01 +0200153 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
154 f_rtpem_init(vc_RTPEM[i], i);
155 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
156 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200157 if (osmux_on) {
158 f_osmuxem_init(vc_OsmuxEM);
159 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
160 }
Philipp Maier55b90542021-07-02 12:33:19 +0200161
Philipp Maier2ff3e662021-08-19 10:52:33 +0200162 f_init_statsd("VirtCallAgent", vc_STATSD, mp_local_ipv4, mp_mgw_statsd_port);
Philipp Maier55b90542021-07-02 12:33:19 +0200163 connect(self:STATSD_PROC, vc_STATSD:STATSD_PROC);
164
165 f_statsd_reset();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800166 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800167
Philipp Maier3560bd62021-08-19 11:52:23 +0200168 f_ipa_ctrl_start_client(mp_remote_ipv4, mp_mgw_ctrl_port);
169
Harald Welteedc45c12017-11-18 19:15:05 +0100170 if (isvalue(ep)) {
171 /* do a DLCX on all connections of the EP */
172 f_dlcx_ignore(valueof(ep));
173 }
Philipp Maier6137c322019-02-20 16:13:41 +0100174
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200175 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800176 }
177
Harald Welte00a067f2017-09-13 23:27:17 +0200178 testcase TC_selftest() runs on dummy_CT {
179 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 +0100180 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 +0200181 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
182 "I: 1\n" &
183 "\n" &
184 "v=0\r\n" &
185 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
186 "s=-\r\n" &
187 "c=IN IP4 0.0.0.0\r\n" &
188 "t=0 0\r\n" &
189 "m=audio 0 RTP/AVP 126\r\n" &
190 "a=rtpmap:126 AMR/8000\r\n" &
191 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100192 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 +0200193 "M: sendrecv\r" &
194 "C: 2\r\n" &
195 "I: 1\r\n" &
196 "L: p:20, a:AMR, nt:IN\r\n" &
197 "\n" &
198 "v=0\r\n" &
199 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800200 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200201 "c=IN IP4 0.0.0.0\r\n" &
202 "t=0 0\r\n" &
203 "m=audio 4441 RTP/AVP 99\r\n" &
204 "a=rtpmap:99 AMR/8000\r\n" &
205 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800206 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200207
208 log(c_auep);
209 log(dec_MgcpCommand(c_auep));
210
211 log(c_mdcx3);
212 log(dec_MgcpCommand(c_mdcx3));
213
214 log(c_mdcx3_ret);
215 log(dec_MgcpResponse(c_mdcx3_ret));
216
217 log(c_mdcx4);
218 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800219
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100220 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
221 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 +0800222
223 log(c_crcx510_ret);
224 log(dec_MgcpResponse(c_crcx510_ret));
225 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100226
227 /* We didn't encounter any DTE, so pass the test */
228 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800229 }
230
Harald Weltee636afd2017-09-17 16:24:09 +0800231 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100232 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800233 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
234 * - CRCX with remote session description and without
235 *
236 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100237 * x packetization != 20ms
238 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800239 * x unsupported mode (517)
240 * x bidirectional mode before RemoteConnDesc: 527
241 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100242 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800243 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
244 */
245
Harald Welte21ba5572017-09-19 17:55:05 +0800246 /* build a receive template for receiving a MGCP message. You
247 * pass the MGCP response template in, and it will generate an
248 * MGCP_RecvFrom template that can match the primitives arriving on the
249 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800250 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
251 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100252 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200253 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800254 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200255 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800256 locPort := mp_local_udp_port,
257 msg := { response := resp }
258 }
259 return mrf;
260 }
261
262 /* Send a MGCP request + receive a (matching!) response */
263 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
264 var MgcpMessage msg := { command := valueof(cmd) };
265 resp.line.trans_id := cmd.line.trans_id;
266 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800267 var MGCP_RecvFrom mrf;
268 timer T := 5.0;
269
Harald Welte55015362017-11-18 16:02:42 +0100270 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800271 T.start;
272 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800273 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200274 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
275 setverdict(fail, "Response didn't match template");
276 mtc.stop;
277 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800278 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200279 [] T.timeout {
280 setverdict(fail, "Timeout waiting for response to ", cmd);
281 mtc.stop;
282 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800283 }
284 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800285
286 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
287 return mrf.msg.response;
288 } else {
289 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
290 return r;
291 }
Harald Welte00a067f2017-09-13 23:27:17 +0200292 }
293
Harald Welteba62c8c2017-11-18 18:26:49 +0100294 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
295 var integer i;
296 for (i := 0; i < lengthof(resp.params); i := i + 1) {
297 var MgcpParameter par := resp.params[i];
298 if (par.code == "I") {
299 return str2hex(par.val);
300 }
301 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200302 setverdict(fail, "Could not find conn id for MgcpReponse");
303 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100304 return '00000000'H;
305 }
306
Harald Welte10889c12017-11-18 19:40:31 +0100307 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
308 template MgcpCallId call_id := omit,
309 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100310 var template MgcpCommand cmd;
311 var MgcpResponse resp;
312 var template MgcpResponse rtmpl := {
313 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100314 code := ret_code,
315 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100316 },
317 params := *,
318 sdp := *
319 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100320 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100321 resp := mgcp_transceive_mgw(cmd, rtmpl);
322 }
323
Harald Welte10889c12017-11-18 19:40:31 +0100324 /* Send DLCX and expect OK response */
325 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
326 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100327 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100328 }
329
Harald Welteba62c8c2017-11-18 18:26:49 +0100330 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100331 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100332 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100333 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100334 }
335
Harald Weltebb7523b2018-03-29 08:52:01 +0200336 type record HostPort {
337 charstring hostname,
338 integer portnr optional
339 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200340 type record RtpOsmuxFlowData {
341 boolean local_cid_sent, /* whther non wildcarded CID was already sent to MGW */
342 MgcpOsmuxCID local_cid optional,
343 MgcpOsmuxCID remote_cid optional,
344 OsmuxemConfig cfg optional
345 }
Philipp Maierbbe454d2023-03-28 15:31:57 +0200346 type record RtpCodecDescr {
347 uint7_t pt,
348 charstring codec,
349 charstring fmtp optional
350 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200351 type record RtpFlowData {
352 HostPort em, /* emulation side */
353 HostPort mgw, /* mgw side */
Philipp Maier28bb8292018-07-20 17:09:17 +0200354 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierbbe454d2023-03-28 15:31:57 +0200355 record of RtpCodecDescr codec_descr,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100356 RtpemConfig rtp_cfg optional,
Philipp Maierbbe454d2023-03-28 15:31:57 +0200357 RtpOsmuxFlowData osmux
358 }
359
Philipp Maierd6c45592023-03-31 16:41:51 +0200360 template RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
361 charstring codec, template charstring fmtp := omit) := {
362 em := {
363 hostname := host_a,
364 portnr := omit
365 },
366 mgw := {
367 hostname := host_b,
368 portnr := omit
369 },
370 codec_descr := {{
371 pt := pt,
372 codec := codec,
373 fmtp := fmtp
374 }},
375 osmux:= {
376 local_cid_sent := false,
377 local_cid := omit,
378 remote_cid := omit,
379 cfg := omit
380 }
381 }
382
Philipp Maierbbe454d2023-03-28 15:31:57 +0200383 /* To be used with f_flow_create/modify... functions */
384 function f_gen_sdp(RtpFlowData flow) runs on dummy_CT return SDP_Message {
385 var template SDP_Message sdp;
386 var SDP_fmt_list fmt_list := {};
387 var SDP_attribute_list attributes := {};
388
389 /* Add SDP RTMAP attributes (codec type, referenced by PT) */
390 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
391 attributes := attributes & { valueof(ts_SDP_rtpmap(flow.codec_descr[i].pt,
392 flow.codec_descr[i].codec)) };
393 }
394
395 /* Add SDP PTIME attribute, regardless of which codec, the packet intervall remains the same */
396 attributes := attributes & { valueof(ts_SDP_ptime(20)) };
397
398 /* Add SDP FMTP attributes (codec parameters for each codec, referenced by PT) */
399 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
400 if (isvalue(flow.codec_descr[i].fmtp) and flow.codec_descr[i].fmtp != "") {
401 attributes := attributes & { valueof(ts_SDP_fmtp(flow.codec_descr[i].pt,
402 flow.codec_descr[i].fmtp)) };
403 }
404 }
405
406 /* Final step: Generate SDP */
407 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
408 fmt_list := fmt_list & {int2str(flow.codec_descr[i].pt)};
409 }
410 sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42", flow.em.portnr, fmt_list, attributes);
411
412 return valueof(sdp);
413 }
414
415 /* Generate a valid RTP emulation config from the payload type numbers configured in the flow description and
416 * the the default configuration of the RTP emulation module. */
417 function f_gen_rtpem_config_from_flow(RtpFlowData flow) return RtpemConfig {
418 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
419
420 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
421 var RtpemConfigPayload tx_cfg_payload;
422 var RtpemConfigPayload rx_cfg_payload;
423
424 tx_cfg_payload.payload_type := flow.codec_descr[i].pt;
425 tx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.tx_payloads[0].fixed_payload;
426 rx_cfg_payload.payload_type := flow.codec_descr[i].pt;
427 rx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.rx_payloads[0].fixed_payload;
428
429 rtp_cfg.tx_payloads := rtp_cfg.tx_payloads & {tx_cfg_payload};
430 rtp_cfg.rx_payloads := rtp_cfg.rx_payloads & {rx_cfg_payload};
431 }
432
433 return rtp_cfg;
Harald Weltebb7523b2018-03-29 08:52:01 +0200434 }
435
Philipp Maier2321ef92018-06-27 17:52:04 +0200436 /* Create an RTP flow (bidirectional, or receive-only) */
437 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 +0200438 boolean one_phase := true)
439 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200440 var template MgcpCommand cmd;
441 var MgcpResponse resp;
442
443 /* bind local RTP emulation socket */
444 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
445
Philipp Maier28bb8292018-07-20 17:09:17 +0200446 /* configure rtp-emulation */
447 if (ispresent(flow.rtp_cfg)) {
448 f_rtpem_configure(pt, flow.rtp_cfg);
449 } else {
Philipp Maierbbe454d2023-03-28 15:31:57 +0200450 f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow));
Philipp Maier28bb8292018-07-20 17:09:17 +0200451 }
452
Harald Weltebb7523b2018-03-29 08:52:01 +0200453 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200454 /* Connect flow to MGW using a CRCX that also contains an SDP
455 * part that tells the MGW where we are listening for RTP streams
456 * that come from the MGW. We get a fully working connection in
457 * one go. */
458
459 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200460 cmd.sdp := f_gen_sdp(flow);
Philipp Maierc8c0b402019-03-07 10:48:45 +0100461
Harald Weltebb7523b2018-03-29 08:52:01 +0200462 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
463 flow.mgcp_conn_id := extract_conn_id(resp);
464 /* extract port number from response */
465 flow.mgw.portnr :=
466 resp.sdp.media_list[0].media_field.ports.port_number;
467 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200468 /* Create a half-open connection only. We do not tell the MGW
469 * where it can send RTP streams to us. This means this
470 * connection will only be able to receive but can not send
471 * data back to us. In order to turn the connection in a fully
472 * bi-directional one, a separate MDCX is needed. */
473
474 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200475 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
476 flow.mgcp_conn_id := extract_conn_id(resp);
477 /* extract MGW-side port number from response */
478 flow.mgw.portnr :=
479 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200480 }
481 /* finally, connect the emulation-side RTP socket to the MGW */
482 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
483 }
484
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200485 /* Create an Osmux flow (bidirectional, or receive-only) */
486 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
487 boolean one_phase := true)
488 runs on dummy_CT {
489 var template MgcpCommand cmd;
490 var MgcpResponse resp;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200491 var OsmuxTxHandle tx_hdl;
492 var OsmuxRxHandle rx_hdl;
493 var charstring cid_response;
494 var OsmuxCID cid_resp_parsed
495
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200496 /* bind local Osmux emulation socket */
497 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
498
499 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200500 if (ispresent(flow.osmux.cfg)) {
501 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200502 } else {
503 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
504 f_osmuxem_configure(pt, osmux_cfg);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200505 flow.osmux.cfg := osmux_cfg
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200506 }
507
508 if (one_phase) {
509 /* Connect flow to MGW using a CRCX that also contains an SDP
510 * part that tells the MGW where we are listening for Osmux streams
511 * that come from the MGW. We get a fully working connection in
512 * one go. */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200513 if (flow.osmux.local_cid != -1) {
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200514 /* We may still want to negotiate osmux CID later at MDCX */
515 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200516 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200517 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200518 flow.osmux.local_cid_sent := true;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200519 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200520 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200521 cmd.sdp := f_gen_sdp(flow);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200522 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
523 flow.mgcp_conn_id := extract_conn_id(resp);
524 /* extract port number from response */
525 flow.mgw.portnr :=
526 resp.sdp.media_list[0].media_field.ports.port_number;
527 } else {
528 /* Create a half-open connection only. We do not tell the MGW
529 * where it can send Osmux streams to us. This means this
530 * connection will only be able to receive but can not send
531 * data back to us. In order to turn the connection in a fully
532 * bi-directional one, a separate MDCX is needed. */
533
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200534 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200535 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
536
537 flow.mgcp_conn_id := extract_conn_id(resp);
538 /* extract MGW-side port number from response */
539 flow.mgw.portnr :=
540 resp.sdp.media_list[0].media_field.ports.port_number;
541 }
542
543 /* extract Osmux CID we got assigned by the MGW */
544 var MgcpMessage resp_msg := {
545 response := resp
546 }
547
548 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
549 setverdict(fail, "No Osmux CID in MGCP response", resp);
550 mtc.stop;
551 }
552
553 /* Make sure response is no wildcard */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200554 flow.osmux.remote_cid := f_mgcp_osmux_cid_decode(cid_response);
555 if (flow.osmux.remote_cid == -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200556 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
557 mtc.stop;
558 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200559 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux.remote_cid));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200560 f_osmuxem_register_txhandle(pt, tx_hdl);
561
562 /* finally, connect the emulation-side RTP socket to the MGW */
563 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
564 }
565
Philipp Maier2321ef92018-06-27 17:52:04 +0200566 /* Modify an existing RTP flow */
567 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
568 runs on dummy_CT {
569 var template MgcpCommand cmd;
570 var MgcpResponse resp;
571
572 /* rebind local RTP emulation socket to the new address */
573 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
574
Philipp Maier28bb8292018-07-20 17:09:17 +0200575 /* reconfigure rtp-emulation */
576 if (ispresent(flow.rtp_cfg)) {
577 f_rtpem_configure(pt, flow.rtp_cfg);
578 } else {
Philipp Maierbbe454d2023-03-28 15:31:57 +0200579 f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow));
Philipp Maier28bb8292018-07-20 17:09:17 +0200580 }
581
Philipp Maier2321ef92018-06-27 17:52:04 +0200582 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
583 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200584 cmd.sdp := f_gen_sdp(flow);
Philipp Maier2321ef92018-06-27 17:52:04 +0200585 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
586
587 /* extract MGW-side port number from response. (usually this
588 * will not change, but thats is up to the MGW) */
589 flow.mgw.portnr :=
590 resp.sdp.media_list[0].media_field.ports.port_number;
591
592 /* reconnect the emulation-side RTP socket to the MGW */
593 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
594 }
595
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200596 /* Modify an existing Osmux flow */
597 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
598 runs on dummy_CT {
599 var template MgcpCommand cmd;
600 var MgcpResponse resp;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200601 var OsmuxRxHandle rx_hdl;
602 var charstring cid_response;
603 var OsmuxCID cid_resp_parsed
604
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200605 /* rebind local Osmux emulation socket to the new address */
606 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
607
608 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200609 if (ispresent(flow.osmux.cfg)) {
610 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200611 } else {
612 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
613 f_osmuxem_configure(pt, osmux_cfg);
614 }
615
616 /* 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 +0200617 if (flow.osmux.local_cid_sent == false and flow.osmux.local_cid != -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200618 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200619 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200620 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200621 flow.osmux.local_cid_sent := true;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200622 }
623
624 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200625 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 +0200626 cmd.sdp := f_gen_sdp(flow);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200627 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
628
629 /* extract MGW-side port number from response. (usually this
630 * will not change, but thats is up to the MGW) */
631 flow.mgw.portnr :=
632 resp.sdp.media_list[0].media_field.ports.port_number;
633
634 /* extract Osmux CID we got assigned by the MGW */
635 var MgcpMessage resp_msg := {
636 response := resp
637 }
638
639 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
640 setverdict(fail, "No Osmux CID in MGCP response", resp);
641 mtc.stop;
642 }
643
644 /* Make sure response is no wildcard */
645 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
646 if (cid_resp_parsed == -1) {
647 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
648 mtc.stop;
649 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200650 if (cid_resp_parsed != flow.osmux.remote_cid) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200651 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
652 mtc.stop;
653 }
654
655 /* reconnect the emulation-side Osmux socket to the MGW */
656 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
657 }
658
Philipp Maier2321ef92018-06-27 17:52:04 +0200659 /* Delete an existing RTP flow */
660 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
661 runs on dummy_CT {
662 var template MgcpCommand cmd;
663 var MgcpResponse resp;
664
665 /* Switch off RTP flow */
666 f_rtpem_mode(pt, RTPEM_MODE_NONE);
667
668 /* Delete connection on MGW (if needed) */
669 if (isvalue(call_id) and isvalue(ep)) {
670 f_sleep(0.1);
671 f_dlcx_ok(valueof(ep), call_id);
672 }
673 }
674
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200675 /* Delete an existing Osmux flow */
676 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
677 runs on dummy_CT {
678 var template MgcpCommand cmd;
679 var MgcpResponse resp;
680
681 /* Switch off Osmux flow */
682 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
683
684 /* Delete connection on MGW (if needed) */
685 if (isvalue(call_id) and isvalue(ep)) {
686 f_sleep(0.1);
687 f_dlcx_ok(valueof(ep), call_id);
688 }
689 }
690
Pau Espin Pedrolb26d4462023-06-15 11:38:02 +0200691 /* Send an AuditEndpoint message to the MGW */
692 function f_auep(charstring ep_prefix) runs on dummy_CT {
693 var MgcpEndpoint ep := ep_prefix & "@" & c_mgw_domain;
694 var template MgcpCommand cmd;
695 var MgcpResponse resp;
696
697 f_init(ep);
698
699 cmd := ts_AUEP(get_next_trans_id(), ep);
700 resp := mgcp_transceive_mgw(cmd, tr_AUEP_ACK);
701 }
702
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100703 function f_crcx(charstring ep_prefix) runs on dummy_CT {
704 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800705 var template MgcpCommand cmd;
706 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100707 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800708
Harald Welteedc45c12017-11-18 19:15:05 +0100709 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800710
Harald Welteba62c8c2017-11-18 18:26:49 +0100711 /* create the connection on the MGW */
712 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100713 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100714 extract_conn_id(resp);
715
716 /* clean-up */
717 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100718 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100719
Philipp Maier45635f42018-06-05 17:28:02 +0200720 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
721 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
722 var template MgcpCommand cmd;
723 var MgcpResponse resp;
724 var MgcpCallId call_id := '1234'H;
725
726 f_init(ep);
727
728 /* create the connection on the MGW */
729 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
730 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
731 extract_conn_id(resp);
732
733 /* clean-up */
734 f_dlcx_ok(ep, call_id);
735
736 /* See also OS#2658: Even when we omit the LCO information, we
737 expect the MGW to pick a sane payload type for us. This
738 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200739 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200740 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200741 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200742 }
743
744 /* See also OS#2658: We also expect the MGW to assign a port
745 number to us. */
746 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
747 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200748 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200749 }
750 }
751
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200752 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
753 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
754 var template MgcpCommand cmd;
755 var MgcpResponse resp;
756 var MgcpCallId call_id := '1234'H;
757 var charstring cid_response;
758
759 if (run_init) {
760 f_init(ep, true);
761 }
762
763 /* create the connection on the MGW */
764 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
765 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
766 extract_conn_id(resp);
767
768 /* extract Osmux CID we got assigned by the MGW */
769 var MgcpMessage resp_msg := {
770 response := resp
771 }
772
773 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
774 setverdict(fail, "No Osmux CID in MGCP response", resp);
775 mtc.stop;
776 }
777
778 /* Make sure response is no wildcard */
779 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
780 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
781 mtc.stop;
782 }
783
784 /* clean-up */
785 f_dlcx_ok(ep, call_id);
786 }
787
Pau Espin Pedrolb26d4462023-06-15 11:38:02 +0200788 /* test valid AUEP towards "null" endpoint */
789 testcase TC_auep_null() runs on dummy_CT {
790 f_auep("null");
791 setverdict(pass);
792 }
793
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100794 /* test valid CRCX without SDP */
795 testcase TC_crcx() runs on dummy_CT {
796 f_crcx(c_mgw_ep_rtpbridge);
797 setverdict(pass);
798 }
799
Philipp Maier45635f42018-06-05 17:28:02 +0200800 /* test valid CRCX without SDP and LCO */
801 testcase TC_crcx_no_lco() runs on dummy_CT {
802 f_crcx_no_lco(c_mgw_ep_rtpbridge);
803 setverdict(pass);
804 }
805
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100806 /* test valid CRCX without SDP (older method without endpoint prefix) */
807 testcase TC_crcx_noprefix() runs on dummy_CT {
808 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800809 setverdict(pass);
810 }
811
812 /* test CRCX with unsupported mode, expect 517 */
813 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
814 var template MgcpCommand cmd;
815 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100816 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100817 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800818 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
819
Harald Welteedc45c12017-11-18 19:15:05 +0100820 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800821
Harald Welteba62c8c2017-11-18 18:26:49 +0100822 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800823 resp := mgcp_transceive_mgw(cmd, rtmpl);
824 setverdict(pass);
825 }
826
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200827 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
828 testcase TC_crcx_osmo_ign() runs on dummy_CT {
829 var template MgcpCommand cmd;
830 var MgcpResponse resp;
831 var MgcpEndpoint ep := "7@" & c_mgw_domain;
832 var MgcpCallId call_id := '3'H;
833
834 f_init(ep);
835
836 /* CRCX 1 7@mgw MGCP 1.0
837 C: 3
838 L: p:20, a:GSM-EFR, nt:IN
839 M: recvonly
840 X-Osmo-IGN: C
841 */
842
843 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
844 cmd.params := {ts_MgcpParCallId(call_id),
845 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
846 t_MgcpParConnMode("recvonly"),
847 t_MgcpParOsmoIGN("C")};
848 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
849 extract_conn_id(resp);
850
851 /* clean-up */
852 f_dlcx_ok(ep, call_id);
853 setverdict(pass);
854 }
855
Harald Welte21ba5572017-09-19 17:55:05 +0800856 /* test CRCX with early bi-directional mode, expect 527 as
857 * bi-diretional media can only be established once both local and
858 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800859 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
860 var template MgcpCommand cmd;
861 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100862 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100863 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800864 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
865
Harald Welteedc45c12017-11-18 19:15:05 +0100866 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800867
Harald Welteba62c8c2017-11-18 18:26:49 +0100868 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800869 resp := mgcp_transceive_mgw(cmd, rtmpl);
870 setverdict(pass);
871 }
872
873 /* test CRCX with unsupported Parameters */
874 testcase TC_crcx_unsupp_param() runs on dummy_CT {
875 var template MgcpCommand cmd;
876 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100877 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100878 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800879 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
880
Harald Welteedc45c12017-11-18 19:15:05 +0100881 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800882
Harald Welteba62c8c2017-11-18 18:26:49 +0100883 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100884 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
885 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
886
Harald Weltee636afd2017-09-17 16:24:09 +0800887 resp := mgcp_transceive_mgw(cmd, rtmpl);
888 setverdict(pass);
889 }
890
891 /* test CRCX with missing CallId */
892 testcase TC_crcx_missing_callid() runs on dummy_CT {
893 var template MgcpCommand cmd;
894 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100895 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100896 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800897
Harald Welteedc45c12017-11-18 19:15:05 +0100898 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800899
Harald Welteba62c8c2017-11-18 18:26:49 +0100900 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800901 cmd.params := {
902 t_MgcpParConnMode("recvonly"),
903 t_MgcpParLocConnOpt("p:20")
904 }
905 resp := mgcp_transceive_mgw(cmd, rtmpl);
906 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100907
Harald Weltee636afd2017-09-17 16:24:09 +0800908 }
909
910 /* test CRCX with missing Mode */
911 testcase TC_crcx_missing_mode() runs on dummy_CT {
912 var template MgcpCommand cmd;
913 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100914 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100915 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100916 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800917
Harald Welteedc45c12017-11-18 19:15:05 +0100918 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800919
Harald Welteba62c8c2017-11-18 18:26:49 +0100920 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800921 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100922 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800923 t_MgcpParLocConnOpt("p:20")
924 }
925 resp := mgcp_transceive_mgw(cmd, rtmpl);
926 setverdict(pass);
927 }
928
929 /* test CRCX with unsupported packetization interval */
930 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
931 var template MgcpCommand cmd;
932 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100933 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100934 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100935 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800936
Harald Welteedc45c12017-11-18 19:15:05 +0100937 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800938
Harald Welteba62c8c2017-11-18 18:26:49 +0100939 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100940 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800941 resp := mgcp_transceive_mgw(cmd, rtmpl);
942 setverdict(pass);
943 }
944
945 /* test CRCX with illegal double presence of local connection option */
946 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
947 var template MgcpCommand cmd;
948 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100949 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100950 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800951 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
952
Harald Welteedc45c12017-11-18 19:15:05 +0100953 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800954
Harald Welteba62c8c2017-11-18 18:26:49 +0100955 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100956 /* p:20 is permitted only once and not twice! */
957 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800958 resp := mgcp_transceive_mgw(cmd, rtmpl);
959 setverdict(pass);
960 }
961
962 /* test valid CRCX with valid SDP */
963 testcase TC_crcx_sdp() runs on dummy_CT {
964 var template MgcpCommand cmd;
965 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100966 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100967 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800968
Harald Welteedc45c12017-11-18 19:15:05 +0100969 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800970
Harald Welteba62c8c2017-11-18 18:26:49 +0100971 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800972 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
973 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
974 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100975 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100976
977 /* clean-up */
978 f_dlcx_ok(ep, call_id);
979
Harald Weltee636afd2017-09-17 16:24:09 +0800980 setverdict(pass);
981 }
982
Philipp Maier5e06cee2018-02-01 18:28:08 +0100983 /* test valid wildcarded CRCX */
984 testcase TC_crcx_wildcarded() runs on dummy_CT {
985 var template MgcpCommand cmd;
986 var MgcpResponse resp;
987 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
988 var MgcpCallId call_id := '1234'H;
989 var MgcpEndpoint ep_assigned;
990 f_init();
991
992 /* create the connection on the MGW */
993 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
994 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
995 extract_conn_id(resp);
996
997 /* extract endpoint name we got assigned by the MGW */
998 var MgcpMessage resp_msg := {
999 response := resp
1000 }
1001 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
1002 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001003 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +01001004 }
1005
1006 /* clean-up */
1007 f_dlcx_ok(ep_assigned, call_id);
1008
1009 setverdict(pass);
1010 }
1011
1012 /* test valid wildcarded CRCX */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001013 type record of MgcpEndpoint MgcpEndpointList;
Philipp Maier5e06cee2018-02-01 18:28:08 +01001014 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maier5e06cee2018-02-01 18:28:08 +01001015 var integer i;
1016 var template MgcpCommand cmd;
1017 var MgcpResponse resp;
1018 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1019 var MgcpCallId call_id := '1234'H;
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001020 var MgcpEndpoint ep_assigned;
1021 var MgcpEndpointList ep_assigned_li := {};
Philipp Maier5e06cee2018-02-01 18:28:08 +01001022 f_init();
1023
1024 /* Exhaust all endpoint resources on the virtual trunk */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001025 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier5e06cee2018-02-01 18:28:08 +01001026 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1027 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1028
1029 /* Make sure we got a connection id */
1030 extract_conn_id(resp);
1031
1032 var MgcpMessage resp_msg := {
1033 response := resp
1034 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001035 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
Philipp Maier5e06cee2018-02-01 18:28:08 +01001036 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001037 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +01001038 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001039 ep_assigned_li := ep_assigned_li & {ep_assigned}
Philipp Maier5e06cee2018-02-01 18:28:08 +01001040 }
1041
1042 /* Try to allocate one more endpoint, which should fail */
1043 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1044 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
1045 resp := mgcp_transceive_mgw(cmd, rtmpl);
1046 setverdict(pass);
1047
1048 /* clean-up */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001049 for (i := 0; i < mp_num_endpoints; i := i+1) {
1050 f_dlcx_ok(ep_assigned_li[i], call_id);
Philipp Maier5e06cee2018-02-01 18:28:08 +01001051 }
1052 setverdict(pass);
1053 }
1054
Harald Weltee636afd2017-09-17 16:24:09 +08001055 /* TODO: various SDP related bits */
1056
1057
1058 /* TODO: CRCX with X-Osmux */
1059 /* TODO: double CRCX without force_realloc */
1060
1061 /* TODO: MDCX (various) */
1062
1063 /* TODO: MDCX without CRCX first */
1064 testcase TC_mdcx_without_crcx() runs on dummy_CT {
1065 var template MgcpCommand cmd;
1066 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001067 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +01001068 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +08001069 var template MgcpResponse rtmpl := {
1070 line := {
1071 /* TODO: accept/enforce better error? */
1072 code := "400",
1073 string := ?
1074 },
1075 params:= { },
1076 sdp := omit
1077 };
1078
Harald Welteedc45c12017-11-18 19:15:05 +01001079 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001080
Harald Welte2bcfd3a2017-11-18 22:14:35 +01001081 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001082 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1083 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1084 valueof(ts_SDP_ptime(20)) });
1085 resp := mgcp_transceive_mgw(cmd, rtmpl);
1086 setverdict(pass);
1087 }
1088
1089 /* DLCX without CRCX first */
1090 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1091 var template MgcpCommand cmd;
1092 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001093 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001094 var template MgcpResponse rtmpl := {
1095 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001096 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001097 string := ?
1098 },
1099 params:= { },
1100 sdp := omit
1101 };
1102
Harald Welteedc45c12017-11-18 19:15:05 +01001103 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001104
Harald Welteedc45c12017-11-18 19:15:05 +01001105 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001106 resp := mgcp_transceive_mgw(cmd, rtmpl);
1107 setverdict(pass);
1108 }
1109
Philipp Maier21c1cff2021-07-20 14:22:53 +02001110 /* DLCX to non existing endpoint */
1111 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1112 var template MgcpCommand cmd;
1113 var MgcpResponse resp;
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001114 var charstring non_existant_ep := hex2str(int2hex(mp_num_endpoints + 1, 4))
1115 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & non_existant_ep & "@" & c_mgw_domain;
Philipp Maier21c1cff2021-07-20 14:22:53 +02001116 var template MgcpResponse rtmpl := {
1117 line := {
1118 code := ("500"),
1119 string := ?
1120 },
1121 params:= { },
1122 sdp := omit
1123 };
1124
1125 f_init(ep);
1126
1127 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1128 resp := mgcp_transceive_mgw(cmd, rtmpl);
1129 setverdict(pass);
1130 }
1131
Philipp Maier8a3dc922018-02-02 14:55:12 +01001132 /* test valid wildcarded MDCX */
1133 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1134 /* Note: A wildcarded MDCX is not allowed, so we expect the
1135 * MGW to reject this request */
1136 var template MgcpCommand cmd;
1137 var MgcpResponse resp;
1138 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1139 var MgcpCallId call_id := '1225'H;
1140 var template MgcpResponse rtmpl := {
1141 line := {
1142 /* TODO: accept/enforce better error? */
1143 code := "507",
1144 string := ?
1145 },
1146 params:= { },
1147 sdp := omit
1148 };
1149
1150 f_init(ep);
1151
1152 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1153 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1154 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1155 valueof(ts_SDP_ptime(20)) });
1156 resp := mgcp_transceive_mgw(cmd, rtmpl);
1157 setverdict(pass);
1158 }
1159
1160 /* test valid wildcarded DLCX */
1161 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001162 var template MgcpCommand cmd;
1163 var MgcpResponse resp;
1164 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001165 var integer i;
1166 var MgcpCallId call_id := '1234'H;
1167 var StatsDExpects expect;
1168 f_init(ep);
1169
1170 /* Allocate a few endpoints */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001171 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier55b90542021-07-02 12:33:19 +02001172 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1173 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1174 }
1175
Philipp Maier1298b092021-11-18 18:33:39 +01001176 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
1177 * occupied endpoints */
1178 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001179 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001180 { 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 +02001181 };
1182 f_statsd_expect(expect);
1183
1184 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001185 var template MgcpResponse rtmpl := {
1186 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001187 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001188 string := ?
1189 },
1190 params:= { },
1191 sdp := omit
1192 };
Philipp Maier55b90542021-07-02 12:33:19 +02001193 cmd := ts_DLCX(get_next_trans_id(), ep);
1194 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001195
Philipp Maier6c740e82022-06-30 12:04:34 +02001196 /* Query a the statsd once to ensure that intermediate results are pulled from the
1197 * pipeline. The second query (below) will return the actual result. */
1198 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001199 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := mp_num_endpoints}
Philipp Maier6c740e82022-06-30 12:04:34 +02001200 };
1201 f_statsd_expect(expect);
1202
1203 /* The second query must resturn a result with 0 endpoints in use. */
Philipp Maier55b90542021-07-02 12:33:19 +02001204 expect := {
1205 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1206 };
1207 f_statsd_expect(expect);
1208
Philipp Maier8a3dc922018-02-02 14:55:12 +01001209 setverdict(pass);
1210 }
1211
Harald Welte79181ff2017-11-18 19:26:11 +01001212 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1213 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1214 var template MgcpCommand cmd;
1215 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001216 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001217 var MgcpCallId call_id := '51234'H;
1218
1219 f_init(ep);
1220
1221 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1222 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1223
1224 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1225
1226 setverdict(pass);
1227 }
1228
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001229 /* test valid CRCX without SDP */
1230 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1231 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1232 setverdict(pass);
1233 }
1234
1235 /* test valid CRCX without SDP */
1236 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1237 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1238 setverdict(pass);
1239 }
1240
1241 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1242 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1243 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1244 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1245 setverdict(pass);
1246 }
1247
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02001248 /* test Creating 257 concurrent osmux conns. It should fail since maximum is 256. */
1249 testcase TC_crcx_osmux_257() runs on dummy_CT {
1250 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1251 var template MgcpCommand cmd;
1252 var MgcpResponse resp;
1253 var charstring cid_response;
1254 var integer i;
1255
1256 f_init(ep, true);
1257
1258 for (i := 0; i < 256; i := i + 1) {
1259
1260 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1261 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
1262 extract_conn_id(resp);
1263
1264 /* extract Osmux CID we got assigned by the MGW */
1265 var MgcpMessage resp_msg := {
1266 response := resp
1267 }
1268
1269 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
1270 setverdict(fail, "No Osmux CID in MGCP response", resp);
1271 mtc.stop;
1272 }
1273
1274 /* Make sure response is no wildcard */
1275 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
1276 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
1277 mtc.stop;
1278 }
1279 }
1280
1281 /* Now conn num 257, it should fail due to all Osmux conns already allocated: */
1282 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1283 resp := mgcp_transceive_mgw(cmd, tr_MgcpResp_Err("400"));
1284
1285 setverdict(pass);
1286
1287 /* Clean up */
1288 for (i := 0; i < 256; i := i + 1) {
1289 f_dlcx_ok(ep, int2hex(i, 4));
1290 }
1291 }
1292
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001293 /* Create one half open connection in receive-only mode. The MGW must accept
1294 * the packets but must not send any. */
1295 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1296 var RtpFlowData flow;
1297 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1298 var MgcpCallId call_id := '1225'H;
1299 var OsmuxemStats stats;
1300 var OsmuxTxHandle tx_hdl;
1301
1302 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001303 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001304 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001305 flow.osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001306 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1307
1308 /* create a transmitter not yet known by MGW */
1309 tx_hdl := valueof(t_TxHandleAMR590(2));
1310 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1311
1312 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1313 f_sleep(1.0);
1314 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1315
1316 stats := f_osmuxem_stats_get(OsmuxEM);
1317
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001318 if (stats.num_pkts_tx < 40 / flow.osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001319 setverdict(fail);
1320 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001321 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 +02001322 setverdict(fail);
1323 }
1324
1325 f_osmuxem_stats_err_check(stats);
1326
1327 setverdict(pass);
1328 }
1329
1330 /* Create one connection in loopback mode, test if the Osmux packets are
1331 * actually reflected */
1332 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1333 var RtpFlowData flow;
1334 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1335 var MgcpCallId call_id := '1225'H;
1336 var OsmuxemStats stats;
1337 var OsmuxTxHandle tx_hdl;
1338
1339 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001340 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001341 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001342 flow.osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001343 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1344
1345 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1346 f_sleep(1.0);
1347
1348 /* Switch off both Tx, wait to receive delayed frames from MGW */
1349 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1350 f_sleep(0.1);
1351 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1352
1353 stats := f_osmuxem_stats_get(OsmuxEM);
1354
1355 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1356 setverdict(fail);
1357 }
1358 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1359 setverdict(fail);
1360 }
1361
1362 f_osmuxem_stats_err_check(stats);
1363
1364 setverdict(pass);
1365 }
1366
1367 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1368 * must match the reception statistics on the other side and vice versa. The
1369 * user may also supply a tolerance value (number of packets) when deviations
1370 * are acceptable */
1371 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1372 var integer plen;
1373
1374 log("stats A: ", a);
1375 log("stats B: ", b);
1376 log("tolerance: ", tolerance, " packets");
1377 log("batch_size: ", batch_size, " packets");
1378
1379 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1380
1381 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1382 return false;
1383 }
1384
1385 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1386 return false;
1387 }
1388
1389 if(a.num_pkts_tx > 0) {
1390 plen := a.bytes_payload_tx / a.num_pkts_tx;
1391 } else {
1392 plen := 0;
1393 }
1394
1395 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1396 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1397 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1398 return false;
1399 }
1400
1401 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) {
1402 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1403 return false;
1404 }
1405
1406 return true;
1407 }
1408
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001409 function f_TC_two_crcx_and_rtp_osmux(boolean bidir, boolean rtp_amr_oa,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001410 charstring local_ip_rtp, charstring remote_ip_rtp,
1411 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001412 var RtpFlowData flow[2];
1413 var RtpemStats stats_rtp;
1414 var OsmuxemStats stats_osmux;
1415 var MgcpResponse resp;
1416 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1417 var MgcpCallId call_id := '1226'H;
1418 var integer tolerance := 0;
1419
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001420 var octetstring amr_payload;
1421 var charstring fmtp;
1422
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001423 f_init(ep, true);
1424
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001425 var AMRFT cmr := AMR_FT_0;
1426 var AMRFT ft := AMR_FT_2;
1427 if (rtp_amr_oa) {
1428 fmtp := "octet-align=1";
1429 var RTP_AMR_Hdr amr_oa_hdr := valueof(ts_RTP_AMR_Hdr(enum2int(cmr), enum2int(ft)));
1430 amr_payload := enc_RTP_AMR_Hdr(amr_oa_hdr) &
1431 f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload);
1432 } else {
1433 fmtp := "octet-align=0";
1434 /* Convert OA to BWE: */
1435 var RTP_AMR_BWE_Hdr amr_bwe_hdr := valueof(ts_RTP_AMR_BWE_Hdr(enum2int(cmr), enum2int(ft)));
1436 var bitstring amr_bwe_hdr_bits := substr(oct2bit(enc_RTP_AMR_BWE_Hdr(amr_bwe_hdr)), 0 , 10);
1437 var bitstring amr_data_bits := oct2bit(f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload));
1438 var bitstring amr_payload_bits := amr_bwe_hdr_bits & substr(amr_data_bits, 0, f_amrft_payload_bits_len(enum2int(ft)));
1439 amr_payload := bit2oct(f_pad_bit(amr_payload_bits, (lengthof(amr_payload_bits)+7)/8*8, '0'B));
1440 };
1441
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001442 /* from us to MGW */
Philipp Maier6b41e152023-03-31 11:45:24 +02001443 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000", fmtp));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001444 flow[0].rtp_cfg := c_RtpemDefaultCfg
Philipp Maierbbe454d2023-03-28 15:31:57 +02001445 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1446 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := amr_payload;
1447 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1448 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := amr_payload;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001449 /* bind local RTP emulation sockets */
1450 flow[0].em.portnr := 10000;
1451 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1452
1453 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001454 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001455 flow[1].em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001456 flow[1].osmux.local_cid := 2;
1457 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001458 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1459
1460 if (bidir) {
1461 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1462 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1463
1464 /* Note: When we test bidirectional we may
1465 * loose packets during switch off because
1466 * both ends are transmitting and we only
1467 * can switch them off one by one. */
1468 tolerance := 3;
1469 } else {
1470 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1471 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1472 }
1473
1474 f_sleep(1.0);
1475
1476 /* Switch off both Tx, wait to receive delayed frames from MGW */
1477 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1478 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1479 f_sleep(0.1);
1480
1481 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1482 f_flow_delete(RTPEM[1]);
1483
1484 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1485 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001486 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 +02001487 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1488 mtc.stop;
1489 }
1490
1491 f_rtpem_stats_err_check(stats_rtp);
1492 f_osmuxem_stats_err_check(stats_osmux);
1493
1494 setverdict(pass);
1495 }
1496
1497 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1498 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001499 f_TC_two_crcx_and_rtp_osmux(false, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001500 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001501 }
1502
1503 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1504 * exchange some data in both directions */
1505 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001506 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001507 mp_local_ipv4, mp_remote_ipv4);
1508 }
1509
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001510 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1511 * exchange some data in both directions. RTP side is configured to
1512 * rx/rx AMR in bandwidth-efficient mode. */
1513 testcase TC_two_crcx_and_rtp_osmux_bidir_amr_bwe() runs on dummy_CT {
1514 f_TC_two_crcx_and_rtp_osmux(true, false, mp_local_ipv4, mp_remote_ipv4,
1515 mp_local_ipv4, mp_remote_ipv4);
1516 }
1517
1518
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001519 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1520 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001521 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001522 mp_local_ipv6, mp_remote_ipv6);
1523 }
1524 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1525 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001526 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001527 mp_local_ipv6, mp_remote_ipv6);
1528 }
1529 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1530 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001531 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001532 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001533 }
1534
1535
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001536 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1537 charstring local_ip_rtp, charstring remote_ip_rtp,
1538 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001539 var RtpFlowData flow[2];
1540 var RtpemStats stats_rtp;
1541 var OsmuxemStats stats_osmux;
1542 var MgcpResponse resp;
1543 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1544 var MgcpCallId call_id := '1227'H;
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001545 var integer num_pkts_tx[2], num_pkts_rx[2];
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001546 var integer temp;
1547
1548 f_init(ep, true);
1549
1550 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001551 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001552 flow[0].rtp_cfg := c_RtpemDefaultCfg
Philipp Maierbbe454d2023-03-28 15:31:57 +02001553 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1554 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001555 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
Philipp Maierbbe454d2023-03-28 15:31:57 +02001556 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);
1557 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 +02001558 /* bind local RTP emulation sockets */
1559 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001560 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001561
1562
1563 /* Create the second connection. This connection will be also
1564 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001565 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001566 flow[1].em.portnr := mp_local_osmux_port;
1567 if (crcx_osmux_wildcard) {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001568 flow[1].osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001569 } else {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001570 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001571 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001572 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001573 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001574
1575
1576 /* The first leg starts transmitting */
1577 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1578 f_sleep(0.5);
1579 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1580 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1581 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1582 mtc.stop;
1583 }
1584 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1585 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1586 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1587 mtc.stop;
1588 }
1589
1590 /* The second leg starts transmitting a little later */
1591 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1592 f_sleep(1.0);
1593 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1594 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1595 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1596 mtc.stop;
1597 }
1598 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1599 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1600 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1601 mtc.stop;
1602 }
1603
1604 /* The first leg will now be switched into bidirectional
1605 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001606 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001607 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001608 /* At this point in time, flow[1](Osmux)->MGW->flow[0](RTP) is active,
1609 * hence if local CID was provided during CRCX we should already be seeing packets
1610 * flowing in one direction, aka stats_rtp.num_pkts_rx sould be >0 after a while: */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001611 f_sleep(0.5);
1612 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1613 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1614 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1615 mtc.stop;
1616 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001617 if (not crcx_osmux_wildcard and stats_rtp.num_pkts_rx == 0) {
1618 setverdict(fail, "received 0 packets Osmux->MGW->RTP");
1619 mtc.stop;
1620 }
1621
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001622 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1623 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1624 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1625 mtc.stop;
1626 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001627 if (stats_osmux.num_pkts_rx > 0) {
1628 setverdict(fail, "received unexpected ", stats_osmux.num_pkts_rx, " packets RTP->MGW->Osmux");
1629 mtc.stop;
1630 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001631
1632 /* When the second leg is switched into bidirectional mode
1633 * as well, then the MGW will connect the two together and
1634 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001635 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001636 if (crcx_osmux_wildcard) {
Pau Espin Pedrol982b9792022-10-07 11:48:13 +02001637 /* We set now the local CID in MDCX: */
1638 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001639 }
1640 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001641 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1642 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1643 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
1644 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
1645 num_pkts_rx[0] := stats_rtp.num_pkts_rx;
1646 num_pkts_rx[1] := stats_osmux.num_pkts_rx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001647 f_sleep(2.0);
1648
1649 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1650 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1651
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001652 temp := (stats_rtp.num_pkts_tx - num_pkts_tx[0]) -
1653 (stats_osmux.num_pkts_rx - num_pkts_rx[1]) * flow[1].osmux.cfg.batch_size;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001654 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 +02001655 log("stats_rtp: ", stats_rtp);
1656 log("stats_osmux: ", stats_osmux);
1657 log("old_rtp_tx: ", num_pkts_tx[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001658 setverdict(fail, "RTP-Tx vs OSmux-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001659 mtc.stop;
1660 }
1661
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001662 temp := (stats_osmux.num_pkts_tx - num_pkts_tx[1]) -
1663 ((stats_rtp.num_pkts_rx - num_pkts_rx[0])/ flow[1].osmux.cfg.batch_size);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001664 if (temp > 3 or temp < -3) {
Pau Espin Pedroldb1e0682022-10-06 19:40:29 +02001665 log("stats_rtp: ", stats_rtp);
1666 log("stats_osmux: ", stats_osmux);
1667 log("old_osmux_tx: ", num_pkts_tx[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001668 setverdict(fail, "Osmux-Tx vs RTP-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001669 mtc.stop;
1670 }
1671
1672 f_rtpem_stats_err_check(stats_rtp);
1673 f_osmuxem_stats_err_check(stats_osmux);
1674
1675 /* Tear down */
1676 f_flow_delete(RTPEM[0]);
1677 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1678 setverdict(pass);
1679 }
1680
1681 /* create one RTP and one OSmux emulations and pass data in both
1682 directions. Create CRCX with wildcard Osmux CID and set it later
1683 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1684 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001685 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1686 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001687 }
1688
1689 /* create one RTP and one OSmux emulations and pass data in both
1690 directions. Create CRCX with fixed Osmux CID and keep it during
1691 MDCX. This is similar to how BSC sets up the call in AoIP. */
1692 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001693 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1694 mp_local_ipv4, mp_remote_ipv4);
1695 }
1696
1697 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1698 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1699 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1700 mp_local_ipv6, mp_remote_ipv6);
1701 }
1702 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1703 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1704 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1705 mp_local_ipv6, mp_remote_ipv6);
1706 }
1707 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1708 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1709 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1710 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001711 }
1712
Harald Welte646ecdb2017-12-28 03:21:57 +01001713 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1714 var template MgcpCommand cmd;
1715 var MgcpResponse resp;
1716
1717 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1718 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1719
1720 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1721
1722 setverdict(pass);
1723 }
1724
1725 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1726 var MgcpEndpoint ep;
1727 var MgcpCallId call_id;
1728 var integer ep_nr;
1729
1730 f_init();
1731
1732 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001733 if(ep_nr > 15) {
1734 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1735 } else {
1736 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1737 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001738 call_id := int2hex(ep_nr, 2) & '1234'H;
1739 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1740 }
1741 }
1742
Harald Welte79181ff2017-11-18 19:26:11 +01001743 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1744 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001745 var template MgcpCommand cmd;
1746 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001747 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001748 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001749
Harald Welteedc45c12017-11-18 19:15:05 +01001750 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001751
Harald Welteba62c8c2017-11-18 18:26:49 +01001752 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001753 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001754
Harald Welteba62c8c2017-11-18 18:26:49 +01001755 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001756
1757 setverdict(pass);
1758 }
1759
Harald Welte79181ff2017-11-18 19:26:11 +01001760 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1761 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1762 var template MgcpCommand cmd;
1763 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001764 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001765 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001766
1767 f_init(ep);
1768
1769 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1770 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1771
1772 f_dlcx_ok(ep);
1773
1774 setverdict(pass);
1775 }
1776
1777
Harald Welte6d167f82017-11-18 19:41:35 +01001778 /* CRCX + DLCX of valid endpoint but invalid call-id */
1779 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1780 var template MgcpCommand cmd;
1781 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001782 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001783 var MgcpCallId call_id := '51231'H;
1784
1785 f_init(ep);
1786
1787 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1788 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1789
1790 f_dlcx(ep, "516", *, 'ffff'H);
1791
1792 setverdict(pass);
1793 }
1794
1795
1796 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1797 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1798 var template MgcpCommand cmd;
1799 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001800 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001801 var MgcpCallId call_id := '51230'H;
1802
1803 f_init(ep);
1804
1805 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1806 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1807
1808 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1809
1810 setverdict(pass);
1811 }
1812
1813
Harald Weltee636afd2017-09-17 16:24:09 +08001814 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001815 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1816 var template MgcpCommand cmd;
1817 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001818 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001819 var MgcpCallId call_id := '51229'H;
1820 var template MgcpResponse rtmpl := {
1821 line := {
1822 code := "200",
1823 string := "OK"
1824 },
1825 params:= { },
1826 sdp := omit
1827 };
1828
1829 f_init(ep);
1830
1831 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1832 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1833
1834 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1835 resp := mgcp_transceive_mgw(cmd, rtmpl);
1836 resp := mgcp_transceive_mgw(cmd, rtmpl);
1837
1838 setverdict(pass);
1839 }
1840
Harald Weltebb7523b2018-03-29 08:52:01 +02001841 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1842 testcase TC_rtpem_selftest() runs on dummy_CT {
1843 var RtpemStats stats[2];
1844 var integer local_port := 10000;
1845 var integer local_port2 := 20000;
1846
1847 f_init();
1848
1849 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1850 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1851
1852 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1853 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1854
1855 log("=== starting");
1856 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1857 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1858
1859 f_sleep(5.0);
1860
1861 log("=== stopping");
1862 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1863 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1864 f_sleep(0.5);
1865 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1866 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1867
1868 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1869 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1870 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1871 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001872 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001873 }
1874 setverdict(pass);
1875 }
1876
Philipp Maier2321ef92018-06-27 17:52:04 +02001877 /* Create one half open connection in receive-only mode. The MGW must accept
1878 * the packets but must not send any. */
1879 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1880 var RtpFlowData flow;
1881 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1882 var MgcpCallId call_id := '1225'H;
1883 var RtpemStats stats;
1884
1885 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001886 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001887 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001888 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001889
1890 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1891 f_sleep(1.0);
1892 f_flow_delete(RTPEM[0], ep, call_id);
1893
1894 stats := f_rtpem_stats_get(RTPEM[0]);
1895
Philipp Maier42b17cc2019-10-01 13:53:17 +02001896 /* Make sure that at least some amount of RTP packets/bytes
1897 * have has been transmitted. The compare values for
1898 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1899 * using a testrun and the results were devided by 2, so even
1900 * in load situations we should reach the minimum amount of
1901 * required packets/bytes */
1902
1903 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001904 setverdict(fail);
1905 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001906 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001907 setverdict(fail);
1908 }
Philipp Maier36291392018-07-25 09:40:44 +02001909
1910 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001911
1912 setverdict(pass);
1913 }
1914
1915 /* Create one connection in loopback mode, test if the RTP packets are
1916 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001917 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 +02001918 var RtpFlowData flow;
1919 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1920 var MgcpCallId call_id := '1225'H;
1921 var RtpemStats stats;
1922
1923 f_init(ep);
Philipp Maier47ec8122023-05-23 11:38:30 +02001924 flow := valueof(t_RtpFlow(local_ip, remote_ip, 112, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001925 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001926 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001927
1928 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1929 f_sleep(1.0);
1930 f_flow_delete(RTPEM[0], ep, call_id);
1931
1932 stats := f_rtpem_stats_get(RTPEM[0]);
1933
Neels Hofmeyr649eba02024-03-27 01:49:52 +01001934 if (one_phase) {
1935 /* osmo-mgw knows both local and remote RTP address. Expect all packets to be reflected. */
1936 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1937 setverdict(fail);
1938 }
1939 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1940 setverdict(fail);
1941 }
1942 } else {
1943 /* osmo-mgw knows only the local RTP address. Expect no packets to be reflected. */
1944 if (stats.num_pkts_rx > 0) {
1945 setverdict(fail, "stats.num_pkts_rx=", stats.num_pkts_rx, ": osmo-mgw should not send RTP packets to an arbitrary peer");
1946 }
1947 if (stats.bytes_payload_rx > 0) {
1948 setverdict(fail);
1949 }
Philipp Maier2321ef92018-06-27 17:52:04 +02001950 }
Philipp Maier36291392018-07-25 09:40:44 +02001951
1952 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001953
1954 setverdict(pass);
1955 }
1956
Philipp Maier1ac13982021-05-07 23:06:07 +02001957 /* Create one connection in loopback mode, test if the RTP packets are
1958 * actually reflected */
1959 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001960 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001961 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001962 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1963 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1964 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001965
1966 /* Same as above, but we will intenionally not tell the MGW where to
1967 * send the outgoing traffic. The connection is still created in
1968 * loopback mode, so the MGW should take the originating address from
1969 * the incoming RTP packet and send it back to the source */
1970 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001971 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001972 }
1973
1974
Philipp Maier7df85f62018-07-25 10:26:09 +02001975 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1976 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001977 var RtpFlowData flow[2];
1978 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001979 var MgcpResponse resp;
1980 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1981 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001982 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001983
1984 f_init(ep);
1985
1986 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001987 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001988 /* bind local RTP emulation sockets */
1989 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001990 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001991
1992 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001993 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001994 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001995 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1996
1997 if (bidir) {
1998 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1999 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2000
2001 /* Note: When we test bidirectional we may
2002 * loose packets during switch off because
2003 * both ends are transmitting and we only
2004 * can switch them off one by one. */
2005 tolerance := 3;
2006 } else {
2007 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2008 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2009 }
2010
2011 f_sleep(1.0);
2012
2013 f_flow_delete(RTPEM[1]);
2014 f_flow_delete(RTPEM[0], ep, call_id);
2015
2016 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2017 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2018 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
2019 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002020 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002021 }
2022
Philipp Maier36291392018-07-25 09:40:44 +02002023 f_rtpem_stats_err_check(stats[0]);
2024 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02002025
Philipp Maier2321ef92018-06-27 17:52:04 +02002026 setverdict(pass);
2027 }
2028
2029 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
2030 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02002031 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02002032 }
2033
2034 /* create two local RTP emulations; create two connections on MGW EP,
2035 * exchange some data in both directions */
2036 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02002037 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
2038 }
2039
2040 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
2041 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
2042 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
2043 }
2044
2045 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
2046 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
2047 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02002048 }
2049
2050 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002051 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002052 charstring local_ip_b, charstring remote_ip_b,
2053 uint7_t payload_type := 3,
2054 charstring codec_name := "GSM/8000/1") runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02002055 var RtpFlowData flow[2];
2056 var RtpemStats stats[2];
2057 var MgcpResponse resp;
2058 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2059 var MgcpCallId call_id := '1227'H;
2060 var integer num_pkts_tx[2];
2061 var integer temp;
2062
2063 f_init(ep);
2064
2065 /* Create the first connection in receive only mode */
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002066 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, payload_type, codec_name));
Philipp Maier2321ef92018-06-27 17:52:04 +02002067 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002068 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02002069
2070 /* Create the second connection. This connection will be also
2071 * in receive only mode */
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002072 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, payload_type, codec_name));
Philipp Maier2321ef92018-06-27 17:52:04 +02002073 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002074 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02002075
2076 /* The first leg starts transmitting */
2077 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2078 f_sleep(0.5);
2079 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2080 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002081 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002082 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002083 }
2084 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2085 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002086 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002087 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002088 }
2089
2090 /* The second leg starts transmitting a little later */
2091 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2092 f_sleep(1.0);
2093 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2094 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002095 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002096 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002097 }
2098 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2099 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002100 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002101 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002102 }
2103
2104 /* The first leg will now be switched into bidirectional
2105 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002106 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2107 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2108 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002109 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2110 f_sleep(0.5);
2111 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002112 if (stats[0].num_pkts_rx_err_disabled != 0) {
2113 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002114 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002115 }
2116 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2117 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002118 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002119 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002120 }
2121
2122 /* When the second leg is switched into bidirectional mode
2123 * as well, then the MGW will connect the two together and
2124 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02002125 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2126 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002127 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002128 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2129 f_sleep(2.0);
2130
2131 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2132 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2133
2134 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2135 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002136 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002137 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002138 }
2139
2140 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2141 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002142 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002143 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002144 }
Philipp Maier36291392018-07-25 09:40:44 +02002145
2146 f_rtpem_stats_err_check(stats[0]);
2147 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02002148
2149 /* Tear down */
2150 f_flow_delete(RTPEM[0]);
2151 f_flow_delete(RTPEM[1], ep, call_id);
2152 setverdict(pass);
2153 }
2154
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002155 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
2156 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2157 mp_local_ipv4, mp_remote_ipv4);
2158 }
2159
2160 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
2161 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
2162 mp_local_ipv6, mp_remote_ipv6);
2163 }
2164
2165 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
2166 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2167 mp_local_ipv6, mp_remote_ipv6);
2168 }
2169
Oliver Smith871f45a2023-01-24 16:21:50 +01002170 testcase TC_two_crcx_mdcx_and_rtp_clearmode() runs on dummy_CT {
2171 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2172 mp_local_ipv4, mp_remote_ipv4,
2173 120, /* 3GPP TS 48.103 table 5.4.2.2.1 */
2174 "CLEARMODE/8000");
2175 }
2176
Philipp Maier2321ef92018-06-27 17:52:04 +02002177 /* Test what happens when two RTP streams from different sources target
2178 * a single connection. Is the unsolicited stream properly ignored? */
2179 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2180 var RtpFlowData flow[2];
2181 var RtpemStats stats[2];
2182 var MgcpResponse resp;
2183 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2184 var MgcpCallId call_id := '1234321326'H;
2185 var integer unsolicited_port := 10002;
2186
2187 f_init(ep);
2188
2189 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002190 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002191 /* bind local RTP emulation sockets */
2192 flow[0].em.portnr := 10000;
2193 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2194
2195 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002196 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002197 flow[1].em.portnr := 20000;
2198 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002199
2200 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2201 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2202
Philipp Maier2321ef92018-06-27 17:52:04 +02002203 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002204
Philipp Maier2321ef92018-06-27 17:52:04 +02002205 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002206 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2207 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002208 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2209
2210 f_sleep(0.5);
2211
Daniel Willmanna069d382018-12-13 13:53:33 +01002212 /* Stop transmitting packets and tear down the flows */
2213 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002214 f_flow_delete(RTPEM[0]);
2215 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002216
2217 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2218 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2219 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2220 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002221 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002222 }
2223
Philipp Maier36291392018-07-25 09:40:44 +02002224 f_rtpem_stats_err_check(stats[0]);
2225 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002226
Harald Weltebb7523b2018-03-29 08:52:01 +02002227 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002228 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002229
Philipp Maier2321ef92018-06-27 17:52:04 +02002230 /* Test a handover situation. We first create two connections transmit
2231 * some data bidirectionally. Then we will simulate a handover situation. */
2232 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2233 var RtpFlowData flow[2];
2234 var RtpemStats stats[3];
2235 var MgcpResponse resp;
2236 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2237 var MgcpCallId call_id := '76338'H;
2238 var integer port_old;
2239
2240 f_init(ep);
2241
2242 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002243 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002244 /* bind local RTP emulation sockets */
2245 flow[0].em.portnr := 10000;
2246 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2247
2248 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002249 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002250 flow[1].em.portnr := 20000;
2251 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2252
2253 /* Normal rtp flow for one second */
2254 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2255 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2256 f_sleep(1.0);
2257
2258 /* Now switch the flow over to a new port (BTS) */
2259 port_old := flow[0].em.portnr;
2260 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002261 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002262 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002263 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002264
2265 /* When handing over a call, the old source may still keep
2266 * transmitting for a while. We simulate this by injecting
2267 * some unsolicited packets on the behalf of the old source,
2268 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002269 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2270 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002271 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2272 f_sleep(1.0);
2273 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2274 f_sleep(1.0);
2275
2276 /* Terminate call */
2277 f_flow_delete(RTPEM[0]);
2278 f_flow_delete(RTPEM[1], ep, call_id);
2279
2280 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2281 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2282 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2283 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002284 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002285 }
2286 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2287 if (stats[2].num_pkts_rx_err_disabled != 0) {
2288 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002289 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002290 }
2291
Philipp Maier36291392018-07-25 09:40:44 +02002292 f_rtpem_stats_err_check(stats[0]);
2293 f_rtpem_stats_err_check(stats[1]);
2294 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002295
Philipp Maier2321ef92018-06-27 17:52:04 +02002296 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002297 }
Harald Weltef53f1642017-11-18 19:57:11 +01002298
Andreas Eversbergdb99cb02023-07-12 11:44:11 +02002299 testcase TC_two_crcx_confecho_sendonly_rtp() runs on dummy_CT {
2300 var RtpFlowData flow[2];
2301 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2302 var MgcpCallId call_id := '1225'H;
2303 var RtpemStats stats[2];
2304
2305 f_init(ep);
2306
2307 /* "Talker" is sending to MGW and receives echo. */
2308 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "GSM-HR-08/8000/1"));
2309 flow[0].em.portnr := 10000;
2310 f_flow_create(RTPEM[0], ep, call_id, "confecho", flow[0]);
2311
2312 /* "Listener" receives from MGW. */
2313 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "GSM-HR-08/8000/1"));
2314 flow[1].em.portnr := 20000;
2315 f_flow_create(RTPEM[1], ep, call_id, "sendonly", flow[1]);
2316
2317
2318 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2319 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2320 f_sleep(1.0);
2321 f_flow_delete(RTPEM[0]);
2322 f_flow_delete(RTPEM[1], ep, call_id);
2323
2324 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2325 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2326
2327 /* The "Talker" will receive his RTP, so TX must match RX.
2328 * As RTP from "Listener" is ignored, no extra packets shall be received. */
2329 if (stats[0].num_pkts_tx != stats[0].num_pkts_rx) {
2330 setverdict(fail, "Talker does not receive as many packets as it transmits!");
2331 }
2332 if (stats[0].bytes_payload_tx != stats[0].bytes_payload_rx) {
2333 setverdict(fail, "Talker does not receive as many payload as it transmits!");
2334 }
2335
2336 /* The "Listener" will also receive RTP of the "Talker",
2337 * so TX of "Talker" must match RX of "Listener". */
2338 if (stats[0].num_pkts_tx != stats[1].num_pkts_rx) {
2339 setverdict(fail, "Listener does not receive as many packets as talker transmits!");
2340 }
2341 if (stats[0].bytes_payload_tx != stats[1].bytes_payload_rx) {
2342 setverdict(fail, "Listener does not receive as many payload as talker transmits!");
2343 }
2344
2345 f_rtpem_stats_err_check(stats[0]);
2346 f_rtpem_stats_err_check(stats[1]);
2347
2348 setverdict(pass);
2349 }
2350
Philipp Maier6d4e0942019-02-21 17:35:01 +01002351
2352 /* create two local RTP emulations; create two connections on MGW EP, see if
Philipp Maier8ed48c52023-02-07 11:24:31 +01002353 * exchanged data is converted between ts101318 and rfc5993 */
2354 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 +01002355 var RtpFlowData flow[2];
2356 var RtpemStats stats[2];
2357 var MgcpResponse resp;
2358 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2359 var MgcpCallId call_id := '1226'H;
2360
2361 f_init(ep);
2362
2363 /* Turn on conversion mode */
2364 f_vty_enter_config(MGWVTY);
2365 f_vty_transceive(MGWVTY, "mgcp");
2366 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2367
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002368 /* Connection #0 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002369 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000", fmtp0));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002370 /* bind local RTP emulation sockets */
2371 flow[0].em.portnr := 10000;
2372 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002373 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2374 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2375 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0;
2376 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002377 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2378
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002379 /* Connection #1 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002380 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000", fmtp1));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002381 flow[1].em.portnr := 20000;
2382 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002383 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2384 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2385 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := pl1;
2386 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := pl1;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002387 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2388
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002389 /* Send RTP packets to connection #0, receive on connection #1 */
2390 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2391 f_sleep(0.5);
2392 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002393 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002394 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2395 f_sleep(0.5);
2396 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002397
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002398 /* Send RTP packets to connection #1, receive on connection #0 */
2399 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2400 f_sleep(0.5);
2401 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2402 f_sleep(1.0);
2403 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2404 f_sleep(0.5);
2405 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2406
2407 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002408 f_flow_delete(RTPEM[0]);
2409 f_flow_delete(RTPEM[1], ep, call_id);
2410
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002411 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002412 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2413 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002414 f_rtpem_stats_err_check(stats[0]);
2415 f_rtpem_stats_err_check(stats[1]);
2416
2417 /* Turn off conversion mode */
2418 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2419
2420 setverdict(pass);
2421 }
2422
Philipp Maier8ed48c52023-02-07 11:24:31 +01002423 const octetstring rtp_hr_gsm_ts101318 := '0b11b3eede60be4e3ec68838c7b5'O;
2424 const octetstring rtp_hr_gsm_rfc5993 := '000b11b3eede60be4e3ec68838c7b5'O;
2425
2426 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2427 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "", "");
2428 }
2429
2430 testcase TC_ts101318_rfc5993_rtp_conversion_fmtp() runs on dummy_CT {
2431 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "gsm-hr-format=ts101318", "gsm-hr-format=rfc5993");
2432 }
2433
Philipp Maier4f764ce2019-03-07 10:54:10 +01002434 /* create two local RTP emulations; create two connections on MGW EP, see if
Philipp Maiereb5e8132023-04-13 16:20:37 +02002435 * exchanged data is converted between AMR octet-aligned and bandwidth
Philipp Maier4f764ce2019-03-07 10:54:10 +01002436 * efficient-mode */
Philipp Maier71484282023-04-04 18:00:53 +02002437 function f_TC_amr_x_x_rtp_conversion(charstring fmtp0, octetstring pl0,
2438 charstring fmtp1a, octetstring pl1a,
2439 charstring fmtp1b, octetstring pl1b) runs on dummy_CT {
Philipp Maier4f764ce2019-03-07 10:54:10 +01002440 var RtpFlowData flow[2];
2441 var RtpemStats stats[2];
2442 var MgcpResponse resp;
2443 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2444 var MgcpCallId call_id := '1226'H;
2445
2446 f_init(ep);
2447
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002448 /* Connection #0 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002449 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 96, "AMR/8000", fmtp0));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002450 /* bind local RTP emulation sockets */
2451 flow[0].em.portnr := 10000;
2452 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002453 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2454 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2455 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0;
2456 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0;
Philipp Maier4f764ce2019-03-07 10:54:10 +01002457 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2458
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002459 /* Connection #1 (Bidirectional) */
Philipp Maier71484282023-04-04 18:00:53 +02002460 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000", fmtp1a));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002461 flow[1].em.portnr := 20000;
2462 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maier71484282023-04-04 18:00:53 +02002463 flow[1].rtp_cfg.rx_payloads := {};
2464 flow[1].rtp_cfg.tx_payloads := {};
2465 if (pl1a != ''O) {
2466 flow[1].rtp_cfg.rx_payloads := flow[1].rtp_cfg.rx_payloads & {{112, pl1a}};
2467 flow[1].rtp_cfg.tx_payloads := flow[1].rtp_cfg.tx_payloads & {{112, pl1a}};
2468 }
2469
2470 /* The second fmtp parameter is to simulate a call agent that offers the transmission both modes. */
2471 if (fmtp1b != "") {
2472 flow[1].codec_descr := flow[1].codec_descr & {{113, "AMR/8000", fmtp1b}};
2473 if (pl1b != ''O) {
2474 flow[1].rtp_cfg.rx_payloads := flow[1].rtp_cfg.rx_payloads & {{113, pl1b}};
2475 flow[1].rtp_cfg.tx_payloads := flow[1].rtp_cfg.tx_payloads & {{113, pl1b}};
2476 }
2477 }
2478
Philipp Maier4f764ce2019-03-07 10:54:10 +01002479 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2480
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002481 /* Send RTP packets to connection #0, receive on connection #1 */
2482 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2483 f_sleep(0.5);
2484 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002485 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002486 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2487 f_sleep(0.5);
2488 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002489
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002490 /* Send RTP packets to connection #1, receive on connection #0 */
2491 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2492 f_sleep(0.5);
2493 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2494 f_sleep(1.0);
2495 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2496 f_sleep(0.5);
2497 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2498
2499 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002500 f_flow_delete(RTPEM[0]);
2501 f_flow_delete(RTPEM[1], ep, call_id);
2502
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002503 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002504 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2505 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002506 f_rtpem_stats_err_check(stats[0]);
2507 f_rtpem_stats_err_check(stats[1]);
2508
2509 setverdict(pass);
2510 }
2511
Philipp Maier882843d2020-05-25 15:33:13 +02002512 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2513 * functions are real world AMR RTP payloads including AMR header. The
2514 * payloads were extracted from a trace with known good payloads. */
2515
Philipp Maiercd756ed2023-02-13 11:12:24 +01002516 const octetstring rtp_amr_5_90k_oa := '2014e959f35fdfe5e9667ffbc088818088'O;
2517 const octetstring rtp_amr_5_90k_bwe := '217a567cd7f7f97a599ffef022206022'O;
2518 const octetstring rtp_amr_5_15k_oa := '100c4e9ba850e30d5d53d04de41e7c'O;
2519 const octetstring rtp_amr_5_15k_bwe := '10d3a6ea1438c35754f41379079f'O;
2520
Philipp Maier71484282023-04-04 18:00:53 +02002521 /* Only one codec on each side */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002522 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier71484282023-04-04 18:00:53 +02002523 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 +01002524 }
Philipp Maier4f764ce2019-03-07 10:54:10 +01002525 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
Philipp Maier71484282023-04-04 18:00:53 +02002526 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa, "octet-align=1", rtp_amr_5_15k_oa, "", ''O);
2527 }
2528 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2529 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 +01002530 }
2531
Philipp Maier71484282023-04-04 18:00:53 +02002532 /* Only one codec on one side, two codecs (compatibility) on other side. The payloads are the same on both
2533 * sides, so the expectation is that conversion must not be performed. Each test is done with both formats in
2534 * two different configurations.*/
2535 testcase TC_amr_oa_oa_no_bwe_rtp_conversion() runs on dummy_CT {
2536 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa,
2537 "octet-align=1", rtp_amr_5_15k_oa,
2538 "octet-align=0", ''O); /* We expect to see NO bandwidth efficient packets! */
2539 }
2540 testcase TC_amr_oa_no_bwe_oa_rtp_conversion() runs on dummy_CT {
2541 /* (Same as above but flipped on the opposite side) */
2542 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa,
2543 "octet-align=0", ''O, /* We expect to see NO bandwidth efficient packets! */
2544 "octet-align=1", rtp_amr_5_15k_oa);
2545 }
2546 testcase TC_amr_bwe_bwe_no_oa_rtp_conversion() runs on dummy_CT {
2547 f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe,
2548 "octet-align=0", rtp_amr_5_15k_bwe,
2549 "octet-align=1", ''O); /* We expect to see NO octet aligned packets! */
2550 }
2551 testcase TC_amr_bwe_no_oa_bwe_rtp_conversion() runs on dummy_CT {
2552 /* (Same as above but flipped on the opposite side) */
2553 f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe,
2554 "octet-align=1", ''O, /* We expect to see NO octet aligned packets! */
2555 "octet-align=0", rtp_amr_5_15k_bwe);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002556 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002557
Harald Weltee636afd2017-09-17 16:24:09 +08002558 /* TODO: Double-DLCX (no retransmission) */
2559
2560
2561
2562 /* TODO: AUEP (various) */
2563 /* TODO: RSIP (various) */
2564 /* TODO: RQNT (various) */
2565 /* TODO: EPCF (various) */
2566 /* TODO: AUCX (various) */
2567 /* TODO: invalid verb (various) */
2568
Oliver Smith021141e2019-06-25 12:09:01 +02002569
2570 testcase TC_conn_timeout() runs on dummy_CT {
2571 var RtpFlowData flow;
2572 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2573 var MgcpCallId call_id := '1225'H;
2574 var MGCP_RecvFrom mrf;
2575
2576 f_init(ep);
2577 log("Setting conn-timeout to 1s");
Pau Espin Pedrol30da2392023-09-27 16:49:20 +02002578 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1");
Oliver Smith021141e2019-06-25 12:09:01 +02002579
2580 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002581 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002582 flow.em.portnr := 10000;
2583 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2584 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2585 f_sleep(1.5);
2586
2587 log("Stopping for 0.5s and resuming");
2588 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2589 f_sleep(0.5);
2590 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2591 f_sleep(0.1);
2592
2593 log("Stopping for 1.5s, expecting to run into timeout");
2594 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2595 f_sleep(1.5);
2596
2597 log("Resuming should fail now");
2598 f_rtpem_conn_refuse_expect(RTPEM[0]);
2599 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2600 f_sleep(0.2);
2601 f_rtpem_conn_refuse_verify(RTPEM[0]);
2602
Pau Espin Pedrol30da2392023-09-27 16:49:20 +02002603 log("Setting conn-timeout back to 0 (disabled)");
2604 f_vty_config(MGWVTY, "mgcp", "conn-timeout 0");
2605
Oliver Smith021141e2019-06-25 12:09:01 +02002606 setverdict(pass);
2607 }
2608
Philipp Maier2609c752020-07-08 12:38:09 +02002609 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2610 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2611 var template MgcpCommand cmd;
2612 var MgcpResponse resp;
2613 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2614 var MgcpCallId call_id := '8376F297'H;
2615
2616 f_init(ep);
2617
2618 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2619 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2620
2621 f_dlcx_ok(ep);
2622
2623 setverdict(pass);
2624 }
2625
2626 /* Test what happens when overlapping endpoints are selected (E1) */
2627 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2628 var template MgcpCommand cmd;
2629 var MgcpResponse resp;
2630 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2631 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2632 var MgcpCallId call_id_1 := '8376F297'H;
2633 var MgcpCallId call_id_2 := '837AF2A7'H;
2634
2635 f_init();
2636
2637 /* ep_1 and ep_2 are overlapping, selecting both one after
2638 * another should work fine: */
2639 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2640 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2641 f_dlcx_ok(ep_1);
2642 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2643 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2644 f_dlcx_ok(ep_2);
2645
2646 /* When ep_1 is serving a call we can not select ep_2 becaus
2647 * it is overlapping with ep_1 */
2648 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2649 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2650 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2651 resp := mgcp_transceive_mgw(cmd, ?);
2652 if (resp.line.code != "501") {
2653 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2654 }
2655 f_dlcx_ok(ep_1);
2656
2657 setverdict(pass);
2658 }
2659
2660 /* Create one connection in loopback mode, test if the RTP packets are
2661 * actually reflected */
2662 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2663 var RtpFlowData flow;
2664 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2665 var MgcpCallId call_id := '12250989'H;
2666 var RtpemStats stats;
2667
2668 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002669 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002670 flow.em.portnr := 10000;
2671 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2672
2673 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2674 f_sleep(1.0);
2675 f_flow_delete(RTPEM[0], ep, call_id);
2676
2677 stats := f_rtpem_stats_get(RTPEM[0]);
2678
2679 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2680 setverdict(fail);
2681 }
2682 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2683 setverdict(fail);
2684 }
2685
2686 f_rtpem_stats_err_check(stats);
2687
2688 setverdict(pass);
2689 }
2690
Philipp Maier13aff992022-06-30 16:19:59 +02002691 /* test valid wildcarded DLCX on an E1 trunk */
2692 testcase TC_e1_dlcx_wildcarded() runs on dummy_CT {
2693 var template MgcpCommand cmd;
2694 var MgcpEndpoint ep;
2695 var MgcpCallId call_id := '8376F297'H;
2696 var integer n_e1_ts := 4;
2697 var StatsDExpects expect;
2698
2699 f_init();
2700
2701 /* Open a few E1 timeslots */
2702 for (var integer i := 0; i < n_e1_ts; i := i+1) {
2703 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-0@" & c_mgw_domain;
2704 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2705 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2706 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-2@" & c_mgw_domain;
2707 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2708 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2709 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-4@" & c_mgw_domain;
2710 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2711 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2712 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-6@" & c_mgw_domain;
2713 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2714 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2715 }
2716
Harald Welte9e8db332024-03-26 15:10:12 +01002717 /* Wait until the stats items have settled and then check if we get the expected number (all) of
Philipp Maier13aff992022-06-30 16:19:59 +02002718 * occupied endpoints */
2719 f_sleep(1.0)
2720 expect := {
2721 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := n_e1_ts * 4, max := n_e1_ts * 4}
2722 };
2723 f_statsd_expect(expect);
2724
2725 /* Send wildcarded DLCX */
2726 var template MgcpResponse rtmpl := {
2727 line := {
2728 code := "200",
2729 string := ?
2730 },
2731 params:= { },
2732 sdp := omit
2733 };
2734 ep := "ds/e1-1/*@" & c_mgw_domain;
2735 cmd := ts_DLCX(get_next_trans_id(), ep);
2736 mgcp_transceive_mgw(cmd, rtmpl);
2737
Harald Welte9e8db332024-03-26 15:10:12 +01002738 /* Query the statsd once to ensure that intermediate results are pulled from the
Philipp Maier13aff992022-06-30 16:19:59 +02002739 * pipeline. The second query (below) will return the actual result. */
2740 expect := {
2741 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := n_e1_ts * 4}
2742 };
2743 f_statsd_expect(expect);
2744
Harald Welte9e8db332024-03-26 15:10:12 +01002745 /* The second query must return a result with 0 endpoints in use. */
Philipp Maier13aff992022-06-30 16:19:59 +02002746 expect := {
2747 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := 0}
2748 };
2749 f_statsd_expect(expect);
2750
2751 setverdict(pass);
2752 }
2753
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002754 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2755 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2756 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2757 var template MgcpCommand cmd;
2758 var MgcpResponse resp;
2759 var MgcpCallId call_id := '1234'H;
2760 var MgcpConnectionId conn_id;
2761
2762 f_init(ep);
2763
2764 /* create the connection on the MGW */
2765 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2766 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2767 conn_id := extract_conn_id(resp);
2768
2769 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2770 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2771 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2772 valueof(ts_SDP_ptime(20)) });
2773 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2774
2775 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2776 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2777 }
2778 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2779 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2780 }
2781
2782 /* clean-up */
2783 f_dlcx_ok(ep, call_id);
2784 setverdict(pass);
2785 }
2786
2787 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2788 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2789 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2790 var template MgcpCommand cmd;
2791 var MgcpResponse resp;
2792 var MgcpCallId call_id := '1234'H;
2793 var MgcpConnectionId conn_id;
2794
2795 f_init(ep);
2796
2797 /* create the connection on the MGW */
2798 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2799 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2800 conn_id := extract_conn_id(resp);
2801
2802 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2803 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2804 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2805 valueof(ts_SDP_ptime(20)) });
2806 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2807
2808 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2809 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2810 }
2811 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2812 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2813 }
2814
2815 /* clean-up */
2816 f_dlcx_ok(ep, call_id);
2817 setverdict(pass);
2818 }
2819
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002820 /* create two local emulations and pass data in both directions */
2821 function f_two_crcx_mdcx_data_transfer(MgcpEndpoint ep, MgcpCallId call_id, inout RtpFlowData flow_a,
2822 inout RtpFlowData flow_b, boolean tear_down_rtp := true) runs on dummy_CT {
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002823 var RtpemStats stats[2];
2824 var MgcpResponse resp;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002825 var integer num_pkts_tx[2];
2826 var integer temp;
2827
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002828 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002829 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow_a, true);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002830 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2831
2832 /* Create the second connection. This connection will be also
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002833 * in receive only mode (CN side, regular RTP) */
2834 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow_b, true);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002835 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2836
2837 /* The first leg starts transmitting */
2838 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2839 f_sleep(0.5);
2840 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2841 if (stats[0].num_pkts_rx_err_disabled != 0) {
2842 setverdict(fail, "received packets from MGW on recvonly connection 0");
2843 mtc.stop;
2844 }
2845 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2846 if (stats[1].num_pkts_rx_err_disabled != 0) {
2847 setverdict(fail, "received packets from MGW on recvonly connection 1");
2848 mtc.stop;
2849 }
2850
2851 /* The second leg starts transmitting a little later */
2852 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2853 f_sleep(1.0);
2854 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2855 if (stats[0].num_pkts_rx_err_disabled != 0) {
2856 setverdict(fail, "received packets from MGW on recvonly connection 0");
2857 mtc.stop;
2858 }
2859 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2860 if (stats[1].num_pkts_rx_err_disabled != 0) {
2861 setverdict(fail, "received packets from MGW on recvonly connection 1");
2862 mtc.stop;
2863 }
2864
2865 /* The first leg will now be switched into bidirectional
2866 * mode, but we do not expect any data coming back yet. */
2867 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2868 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2869 num_pkts_tx[1] := stats[1].num_pkts_tx;
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002870 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow_a);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002871 f_sleep(0.5);
2872 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2873 if (stats[0].num_pkts_rx_err_disabled != 0) {
2874 setverdict(fail, "received packets from MGW on recvonly connection 0");
2875 mtc.stop;
2876 }
2877 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2878 if (stats[1].num_pkts_rx_err_disabled != 0) {
2879 setverdict(fail, "received packets from MGW on recvonly connection 1");
2880 mtc.stop;
2881 }
2882
2883 /* When the second leg is switched into bidirectional mode
2884 * as well, then the MGW will connect the two together and
2885 * we should see RTP streams passing through from both ends. */
2886 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2887 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2888 num_pkts_tx[0] := stats[0].num_pkts_tx;
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002889 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow_b);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002890 f_sleep(2.0);
2891
2892 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2893 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2894
2895 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2896 if (temp > 3 or temp < -3) {
2897 setverdict(fail, "number of packets not within normal parameters:", temp);
2898 mtc.stop;
2899 }
2900
2901 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2902 if (temp > 3 or temp < -3) {
2903 setverdict(fail, "number of packets not within normal parameters:", temp);
2904 mtc.stop;
2905 }
2906
2907 f_rtpem_stats_err_check(stats[0]);
2908 f_rtpem_stats_err_check(stats[1]);
2909
2910 /* Tear down */
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002911 if (tear_down_rtp) {
2912 f_flow_delete(RTPEM[0]);
2913 f_flow_delete(RTPEM[1], ep, call_id);
2914 }
2915 setverdict(pass);
2916 }
2917
2918 /* create two local RTP+IuUP emulations and pass data in both directions */
2919 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
2920 IuUP_RabFlowCombinationList rfcl_a,
2921 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2922 var RtpFlowData flow[2];
2923 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2924 var MgcpCallId call_id := '1227'H;
2925
2926 f_init(ep);
2927
2928 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2929 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2930 flow[0].em.portnr := 10000;
2931 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2932 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2933 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2934 flow[0].rtp_cfg.iuup_mode := true;
2935 flow[0].rtp_cfg.iuup_cfg.active_init := true;
2936 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
2937
2938 /* Create the second connection. This connection will be also
2939 * in receive only mode (CN side, IuUP-Init passive) */
2940 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2941 flow[1].em.portnr := 20000;
2942 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2943 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2944 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2945 flow[1].rtp_cfg.iuup_mode := true;
2946 flow[1].rtp_cfg.iuup_cfg.active_init := false;
2947
2948 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], true);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002949 setverdict(pass);
2950 }
2951 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002952 var template (value) IuUP_RabFlowCombinationList rfcl := {
2953 t_IuUP_RFC_AMR_12_2(0),
2954 t_IuUP_RFC_AMR_SID(1),
2955 t_IuUP_RFC_AMR_NO_DATA(2)
2956 };
2957 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2958 mp_local_ipv4, mp_remote_ipv4);
2959 }
2960 /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */
2961 testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT {
2962 var template (value) IuUP_RabFlowCombinationList rfcl := {
2963 t_IuUP_RFC_AMR_12_2(1),
2964 t_IuUP_RFC_AMR_SID(2),
2965 t_IuUP_RFC_AMR_NO_DATA(0)
2966 };
2967 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002968 mp_local_ipv4, mp_remote_ipv4);
2969 }
2970
Pau Espin Pedrole6d095c2023-09-27 14:47:45 +02002971 /* Test that once IuUP->RTP has been set up, if the RTP/IuUP conn is set
2972 * as "recvonly", no more RTP/IuUP packets get out of the MGW. */
2973 function f_tc_two_crcx_mdcx_and_iuup_mdcx_recvonly(charstring local_ip_a, charstring remote_ip_a,
2974 IuUP_RabFlowCombinationList rfcl_a,
2975 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2976 var RtpFlowData flow[2];
2977 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2978 var MgcpCallId call_id := '1227'H;
2979 var RtpemStats stats[2];
2980
2981 f_init(ep);
2982
2983 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2984 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2985 flow[0].em.portnr := 10000;
2986 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2987 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2988 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2989 flow[0].rtp_cfg.iuup_mode := true;
2990 flow[0].rtp_cfg.iuup_cfg.active_init := true;
2991 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
2992
2993 /* Create the second connection. This connection will be also
2994 * in receive only mode (CN side, IuUP-Init passive) */
2995 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2996 flow[1].em.portnr := 20000;
2997 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2998 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2999 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3000 flow[1].rtp_cfg.iuup_mode := true;
3001 flow[1].rtp_cfg.iuup_cfg.active_init := false;
3002
3003 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], false);
3004
3005 /* Now validate we don't receive more RTP packets after setting it to recvonly: */
3006 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
3007 f_flow_modify(RTPEM[1], ep, call_id, "recvonly", flow[1]);
3008 f_sleep(0.5);
3009 stats[0] := f_rtpem_stats_get(RTPEM[1]);
3010 f_sleep(0.5);
3011 stats[1] := f_rtpem_stats_get(RTPEM[1]);
3012 if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) {
3013 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3014 log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " RTP packets from MGW on recvonly connection 1"));
3015 }
3016
3017 /* Now do the same on the IuUP port: */
3018 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
3019 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
3020 f_flow_modify(RTPEM[0], ep, call_id, "recvonly", flow[0]);
3021 f_sleep(0.5);
3022 stats[0] := f_rtpem_stats_get(RTPEM[0]);
3023 f_sleep(0.5);
3024 stats[1] := f_rtpem_stats_get(RTPEM[0]);
3025 if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) {
3026 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3027 log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " IuUP packets from MGW on recvonly connection 1"));
3028 }
3029
3030 /* Tear down */
3031 f_flow_delete(RTPEM[0]);
3032 f_flow_delete(RTPEM[1], ep, call_id);
3033 setverdict(pass);
3034 }
3035 testcase TC_two_crcx_mdcx_and_iuup_mdcx_recvonly() runs on dummy_CT {
3036 var template (value) IuUP_RabFlowCombinationList rfcl := {
3037 t_IuUP_RFC_AMR_12_2(0),
3038 t_IuUP_RFC_AMR_SID(1),
3039 t_IuUP_RFC_AMR_NO_DATA(2)
3040 };
3041 f_tc_two_crcx_mdcx_and_iuup_mdcx_recvonly(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
3042 mp_local_ipv4, mp_remote_ipv4);
3043 }
3044
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003045 /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */
3046 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 +02003047 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003048 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
3049 var RtpFlowData flow[2];
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003050 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
3051 var MgcpCallId call_id := '1227'H;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003052
3053 f_init(ep);
3054
3055 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
3056 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
3057 flow[0].em.portnr := 10000;
3058 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02003059 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3060 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3061 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
3062 /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
3063 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003064 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02003065 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003066 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003067
3068 /* Create the second connection. This connection will be also
3069 * in receive only mode (CN side, regular RTP) */
3070 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
3071 flow[1].em.portnr := 20000;
3072 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02003073 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3074 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3075 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
3076 /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
3077 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003078 flow[1].rtp_cfg.iuup_mode := false;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003079
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02003080 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], true);
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003081 setverdict(pass);
3082 }
3083 testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003084 var template (value) IuUP_RabFlowCombinationList rfcl := {
3085 t_IuUP_RFC_AMR_12_2(0),
3086 t_IuUP_RFC_AMR_SID(1),
3087 t_IuUP_RFC_AMR_NO_DATA(2)
3088 };
3089 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
3090 mp_local_ipv4, mp_remote_ipv4);
3091 }
3092 /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */
3093 testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT {
3094 var template (value) IuUP_RabFlowCombinationList rfcl := {
3095 t_IuUP_RFC_AMR_12_2(1),
3096 t_IuUP_RFC_AMR_SID(2),
3097 t_IuUP_RFC_AMR_NO_DATA(0)
3098 };
3099 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003100 mp_local_ipv4, mp_remote_ipv4);
3101 }
3102
Pau Espin Pedrole6d095c2023-09-27 14:47:45 +02003103 /* Test that once IuUP->RTP has been set up, if the RTP/IuUP conn is set
3104 * as "recvonly", no more RTP/IuUP packets get out of the MGW. */
3105 function f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly(charstring local_ip_a, charstring remote_ip_a,
3106 IuUP_RabFlowCombinationList rfcl_a,
3107 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
3108 var RtpFlowData flow[2];
3109 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
3110 var MgcpCallId call_id := '1227'H;
3111 var RtpemStats stats[2];
3112
3113 f_init(ep);
3114
3115 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
3116 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
3117 flow[0].em.portnr := 10000;
3118 flow[0].rtp_cfg := c_RtpemDefaultCfg;
3119 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3120 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3121 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
3122 /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
3123 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O;
3124 flow[0].rtp_cfg.iuup_mode := true;
3125 flow[0].rtp_cfg.iuup_cfg.active_init := true;
3126 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
3127
3128 /* Create the second connection. This connection will be also
3129 * in receive only mode (CN side, regular RTP) */
3130 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
3131 flow[1].em.portnr := 20000;
3132 flow[1].rtp_cfg := c_RtpemDefaultCfg;
3133 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3134 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3135 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
3136 /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
3137 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O;
3138 flow[1].rtp_cfg.iuup_mode := false;
3139
3140 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], false);
3141
3142 /* Now validate we don't receive more RTP packets after setting it to recvonly: */
3143 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
3144 f_flow_modify(RTPEM[1], ep, call_id, "recvonly", flow[1]);
3145 f_sleep(0.5);
3146 stats[0] := f_rtpem_stats_get(RTPEM[1]);
3147 f_sleep(0.5);
3148 stats[1] := f_rtpem_stats_get(RTPEM[1]);
3149 if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) {
3150 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3151 log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " RTP packets from MGW on recvonly connection 1"));
3152 }
3153
3154 /* Now do the same on the IuUP port: */
3155 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
3156 f_flow_modify(RTPEM[0], ep, call_id, "recvonly", flow[0]);
3157 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
3158 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
3159 f_sleep(0.5);
3160 stats[0] := f_rtpem_stats_get(RTPEM[0]);
3161 f_sleep(0.5);
3162 stats[1] := f_rtpem_stats_get(RTPEM[0]);
3163 if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) {
3164 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3165 log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " IuUP packets from MGW on recvonly connection 1"));
3166 }
3167
3168 /* Tear down */
3169 f_flow_delete(RTPEM[0]);
3170 f_flow_delete(RTPEM[1], ep, call_id);
3171 setverdict(pass);
3172 }
3173 testcase TC_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly() runs on dummy_CT {
3174 var template (value) IuUP_RabFlowCombinationList rfcl := {
3175 t_IuUP_RFC_AMR_12_2(0),
3176 t_IuUP_RFC_AMR_SID(1),
3177 t_IuUP_RFC_AMR_NO_DATA(2)
3178 };
3179 f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
3180 mp_local_ipv4, mp_remote_ipv4);
3181 }
3182
Pau Espin Pedrol0bc02122023-09-27 14:48:10 +02003183 /* Set up Endpoint with 1 IuUP conn and 1 RTP-AMR conn, then MDCX the later to become IuUP. */
3184 function f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup(charstring local_ip_a, charstring remote_ip_a,
3185 IuUP_RabFlowCombinationList rfcl_a,
3186 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
3187 var RtpFlowData flow[2];
3188 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
3189 var MgcpCallId call_id := '1227'H;
3190 var RtpemStats stats[2][2];
3191
3192 f_init(ep);
3193
3194 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
3195 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
3196 flow[0].em.portnr := 10000;
3197 flow[0].rtp_cfg := c_RtpemDefaultCfg;
3198 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3199 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
3200 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
3201 /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
3202 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O;
3203 flow[0].rtp_cfg.iuup_mode := true;
3204 flow[0].rtp_cfg.iuup_cfg.active_init := true;
3205 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
3206
3207 /* Create the second connection. This connection will be also
3208 * in receive only mode (CN side, regular RTP) */
3209 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
3210 flow[1].em.portnr := 20000;
3211 flow[1].rtp_cfg := c_RtpemDefaultCfg;
3212 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3213 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3214 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
3215 /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
3216 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O;
3217 flow[1].rtp_cfg.iuup_mode := false;
3218
3219 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], false);
3220 setverdict(pass);
3221
3222 /* Stop sending further pkts to RTPEM[1], to avoid race conditions where RTP is sent while we
3223 * reconfigure MGW (and our RTPEM) for IuUP:
3224 */
3225 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
3226 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
3227 f_sleep(0.5);
3228
3229 /* Modify the AMR side to also do IuUP */
3230 flow[1].codec_descr := {{
3231 pt := 96,
3232 codec := "VND.3GPP.IUFP/16000",
3233 fmtp := omit
3234 }};
3235 flow[1].rtp_cfg.iuup_mode := true;
3236 flow[1].rtp_cfg.iuup_cfg.active_init := false;
3237 flow[1].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
3238 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3239 /* Send whatever payload is expected by the other RTPEM, now without AMR-BE_RTP<->AMR-IUUP conversion: */
3240 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := flow[0].rtp_cfg.rx_payloads[0].fixed_payload;
3241 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
3242 /* Expect whatever payload is sent by the other RTPEM, now without AMR-BE_RTP<->AMR-IUUP conversion: */
3243 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := flow[0].rtp_cfg.tx_payloads[0].fixed_payload;
3244 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
3245 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
3246 f_sleep(1.0);
3247 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
3248
3249 /* Now, if MGW would keep sending RTP instead of expected IuUP, then that would make the RTPEM[1]
3250 * component to fail, since it would error trying to decode an RTP pkt as IuUP.
3251 * We simply need to make sure some packets are being forwarded to it:
3252 */
3253 stats[0][0] := f_rtpem_stats_get(RTPEM[0]);
3254 stats[1][0] := f_rtpem_stats_get(RTPEM[1]);
3255 f_sleep(1.0);
3256 stats[0][1] := f_rtpem_stats_get(RTPEM[0]);
3257 stats[1][1] := f_rtpem_stats_get(RTPEM[1]);
3258
3259 f_rtpem_stats_err_check(stats[0][1]);
3260 f_rtpem_stats_err_check(stats[1][1]);
3261 if (stats[0][1].num_pkts_rx == stats[0][0].num_pkts_rx) {
3262 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3263 log2str("No packets received in connection 0 after MDCX"));
3264 }
3265 if (stats[1][1].num_pkts_rx == stats[1][0].num_pkts_rx) {
3266 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
3267 log2str("No packets received in connection 1 after MDCX"));
3268 }
3269
3270 /* Tear down */
3271 f_flow_delete(RTPEM[0]);
3272 f_flow_delete(RTPEM[1], ep, call_id);
3273 setverdict(pass);
3274 }
3275 testcase TC_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup() runs on dummy_CT {
3276 var template (value) IuUP_RabFlowCombinationList rfcl := {
3277 t_IuUP_RFC_AMR_12_2(0),
3278 t_IuUP_RFC_AMR_SID(1),
3279 t_IuUP_RFC_AMR_NO_DATA(2)
3280 };
3281 f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
3282 mp_local_ipv4, mp_remote_ipv4);
3283 }
Harald Welte00a067f2017-09-13 23:27:17 +02003284 control {
3285 execute(TC_selftest());
Pau Espin Pedrolb26d4462023-06-15 11:38:02 +02003286 execute(TC_auep_null());
Harald Welte3c6ebb92017-09-16 00:56:57 +08003287 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02003288 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01003289 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08003290 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02003291 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08003292 execute(TC_crcx_early_bidir_mode());
3293 execute(TC_crcx_unsupp_param());
3294 execute(TC_crcx_missing_callid());
3295 execute(TC_crcx_missing_mode());
3296 execute(TC_crcx_unsupp_packet_intv());
3297 execute(TC_crcx_illegal_double_lco());
3298 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01003299 execute(TC_crcx_wildcarded());
3300 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08003301 execute(TC_mdcx_without_crcx());
3302 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02003303 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01003304 execute(TC_mdcx_wildcarded());
3305 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01003306 execute(TC_crcx_and_dlcx_ep_callid_connid());
3307 execute(TC_crcx_and_dlcx_ep_callid());
3308 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01003309 execute(TC_crcx_and_dlcx_ep_callid_inval());
3310 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01003311 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01003312
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003313 execute(TC_crcx_osmux_wildcard());
3314 execute(TC_crcx_osmux_fixed());
3315 execute(TC_crcx_osmux_fixed_twice());
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02003316 execute(TC_crcx_osmux_257());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003317 execute(TC_one_crcx_receive_only_osmux());
3318 execute(TC_one_crcx_loopback_osmux());
3319 execute(TC_two_crcx_and_rtp_osmux());
3320 execute(TC_two_crcx_and_rtp_osmux_bidir());
Pau Espin Pedrol26258472022-10-25 12:51:24 +02003321 execute(TC_two_crcx_and_rtp_osmux_bidir_amr_bwe());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003322 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
3323 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
3324
Harald Welte33d82162017-12-28 03:21:57 +01003325 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02003326
3327 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02003328
3329 execute(TC_one_crcx_receive_only_rtp());
3330 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02003331 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02003332 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02003333 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02003334 execute(TC_two_crcx_diff_pt_and_rtp());
3335 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02003336 execute(TC_two_crcx_mdcx_and_rtp());
3337 execute(TC_two_crcx_and_unsolicited_rtp());
3338 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Andreas Eversbergdb99cb02023-07-12 11:44:11 +02003339 execute(TC_two_crcx_confecho_sendonly_rtp());
Philipp Maier6d4e0942019-02-21 17:35:01 +01003340 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier8ed48c52023-02-07 11:24:31 +01003341 execute(TC_ts101318_rfc5993_rtp_conversion_fmtp());
Philipp Maier4f764ce2019-03-07 10:54:10 +01003342 execute(TC_amr_oa_bwe_rtp_conversion());
3343 execute(TC_amr_oa_oa_rtp_conversion());
3344 execute(TC_amr_bwe_bwe_rtp_conversion());
Philipp Maier71484282023-04-04 18:00:53 +02003345 execute(TC_amr_oa_oa_no_bwe_rtp_conversion());
3346 execute(TC_amr_oa_no_bwe_oa_rtp_conversion());
3347 execute(TC_amr_bwe_bwe_no_oa_rtp_conversion());
3348 execute(TC_amr_bwe_no_oa_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02003349
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01003350 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02003351
3352 execute(TC_e1_crcx_and_dlcx_ep());
3353 execute(TC_e1_crcx_with_overlap());
3354 execute(TC_e1_crcx_loopback());
Philipp Maier13aff992022-06-30 16:19:59 +02003355 execute(TC_e1_dlcx_wildcarded());
Philipp Maier2609c752020-07-08 12:38:09 +02003356
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02003357 execute(TC_crcx_mdcx_ip4());
3358 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02003359 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
3360 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
3361 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
3362 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
3363 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
3364 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
3365 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
3366 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02003367
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003368 execute(TC_two_crcx_mdcx_and_iuup());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003369 execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered());
Pau Espin Pedrole6d095c2023-09-27 14:47:45 +02003370 execute(TC_two_crcx_mdcx_and_iuup_mdcx_recvonly());
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003371 execute(TC_two_crcx_mdcx_and_iuup_rtp());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003372 execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered());
Pau Espin Pedrole6d095c2023-09-27 14:47:45 +02003373 execute(TC_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly());
Pau Espin Pedrol0bc02122023-09-27 14:48:10 +02003374 execute(TC_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup());
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003375
Oliver Smith871f45a2023-01-24 16:21:50 +01003376 execute(TC_two_crcx_mdcx_and_rtp_clearmode());
3377
Philipp Maier37965082021-05-25 16:44:25 +02003378 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
3379 * older versions of osmo-mgw. This eventually leads into
3380 * a failure of all subsequent testcases, so it is important
3381 * not to add new testcaes after this one. */
3382 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02003383 }
3384}