blob: ad564c1a270c081a8b82d0591029ae9fa31e078a [file] [log] [blame]
Harald Welte34b5a952019-05-27 11:54:11 +02001/* MGW (Media Gateway) test suite in TTCN-3
2 * (C) 2017-2018 Harald Welte <laforge@gnumonks.org>
3 * (C) 2018-2019 sysmocom - s.f.m.c. GmbH
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
Harald Welte00a067f2017-09-13 23:27:17 +020012module MGCP_Test {
Harald Weltef07c2862017-11-18 17:16:24 +010013 import from Osmocom_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +020014 import from MGCP_Types all;
Harald Welte4029e8c2017-11-23 22:00:42 +010015 import from MGCP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080016 import from SDP_Types all;
17 import from MGCP_CodecPort all;
18 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +010019 import from RTP_CodecPort all;
20 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020021 import from RTP_Emulation all;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +020022 import from IuUP_Emulation all;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020023 import from OSMUX_Types all;
24 import from OSMUX_CodecPort all;
25 import from OSMUX_CodecPort_CtrlFunct all;
26 import from OSMUX_Emulation all;
Pau Espin Pedrol71ed4632022-09-02 18:51:19 +020027 import from AMR_Types all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080028 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010029 import from General_Types all;
30 import from Native_Functions all;
31 import from IPCP_Types all;
32 import from IP_Types all;
33 import from Osmocom_VTY_Functions all;
34 import from TELNETasp_PortType all;
Philipp Maier55b90542021-07-02 12:33:19 +020035 import from StatsD_Types all;
36 import from StatsD_CodecPort all;
37 import from StatsD_CodecPort_CtrlFunct all;
38 import from StatsD_Checker all;
Philipp Maier3560bd62021-08-19 11:52:23 +020039 import from Osmocom_CTRL_Functions all;
40 import from Osmocom_CTRL_Types all;
41 import from Osmocom_CTRL_Adapter all;
Harald Welte00a067f2017-09-13 23:27:17 +020042
Philipp Maierbb7a01c2018-02-01 12:32:57 +010043 const charstring c_mgw_domain := "mgw";
44 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
45
Harald Welte21ba5572017-09-19 17:55:05 +080046 /* any variables declared in the component will be available to
47 * all functions that 'run on' the named component, similar to
48 * class members in C++ */
Philipp Maier3560bd62021-08-19 11:52:23 +020049 type component dummy_CT extends StatsD_ConnHdlr, CTRL_Adapter_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080050 port MGCP_CODEC_PT MGCP;
51 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010052 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080053 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010054
Philipp Maier2321ef92018-06-27 17:52:04 +020055 var RTP_Emulation_CT vc_RTPEM[3];
56 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010057
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020058 var OSMUX_Emulation_CT vc_OsmuxEM;
59 port OsmuxEM_CTRL_PT OsmuxEM;
60
Philipp Maier6137c322019-02-20 16:13:41 +010061 port TELNETasp_PT MGWVTY;
Philipp Maier55b90542021-07-02 12:33:19 +020062
63 var StatsD_Checker_CT vc_STATSD;
Harald Welte00a067f2017-09-13 23:27:17 +020064 };
65
Harald Weltee1e18c52017-09-17 16:23:07 +080066 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
67 var MgcpTransId tid := int2str(g_trans_id);
68 g_trans_id := g_trans_id + 1;
69 return tid;
70 }
71
Harald Welte21ba5572017-09-19 17:55:05 +080072 /* all parameters declared here can be modified / overridden by
73 * the config file in the [MODULE_PARAMETERS] section. If no
74 * config file is used or the file doesn't specify them, the
75 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080076 modulepar {
77 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020078 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020079 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080080 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020081 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020082 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010083 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020084 PortNumber mp_local_osmux_port := 1985;
Philipp Maier55b90542021-07-02 12:33:19 +020085 PortNumber mp_mgw_statsd_port := 8125;
Philipp Maier3560bd62021-08-19 11:52:23 +020086 PortNumber mp_mgw_ctrl_port := 4267;
Pau Espin Pedrole7928872022-10-07 10:57:11 +020087 /* Maximum number of available endpoints in osmo-mgw.cfg ("number endpoints"): */
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +020088 integer mp_num_endpoints := 300;
Harald Welte3c6ebb92017-09-16 00:56:57 +080089 }
90
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020091 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
92 /* Turn on conversion mode */
93 f_vty_enter_config(MGWVTY);
94 f_vty_transceive(MGWVTY, "mgcp");
95 if (osmux_on) {
96 f_vty_transceive(MGWVTY, "osmux on");
97 } else {
98 f_vty_transceive(MGWVTY, "osmux off");
99 }
100 f_vty_transceive(MGWVTY, "exit");
101 f_vty_transceive(MGWVTY, "exit");
102
103 }
104
105 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +0100106 map(self:MGWVTY, system:MGWVTY);
107 f_vty_set_prompts(MGWVTY);
108 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200109
110 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +0100111 }
112
Harald Weltebb7523b2018-03-29 08:52:01 +0200113 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
114 runs on dummy_CT {
115 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
116 map(comp_ref:RTP, system:RTP);
117 map(comp_ref:RTCP, system:RTCP);
118 comp_ref.start(RTP_Emulation.f_main());
119 }
120
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200121 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
122 runs on dummy_CT {
123 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
124 map(comp_ref:OSMUX, system:OSMUX);
125 comp_ref.start(OSMUX_Emulation.f_main());
126 }
127
Harald Welte21ba5572017-09-19 17:55:05 +0800128 /* initialization function, called by each test case at the
129 * beginning, but 'initialized' variable ensures its body is
130 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200131 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800132 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100133 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200134
Harald Welteedc45c12017-11-18 19:15:05 +0100135 if (initialized == false) {
136 initialized := true;
137
138 /* some random number for the initial transaction id */
139 g_trans_id := float2int(rnd()*65535.0);
140 map(self:MGCP, system:MGCP_CODEC_PT);
141 /* connect the MGCP test port using the given
142 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
143 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200144 res := MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, mp_remote_ipv4, mp_remote_udp_port, mp_local_ipv4, mp_local_udp_port, 0, { udp := {} });
Harald Welte9220f632018-05-23 20:27:02 +0200145 if (not ispresent(res.connId)) {
146 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200147 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200148 }
Harald Welteedc45c12017-11-18 19:15:05 +0100149 g_mgcp_conn_id := res.connId;
150
Harald Weltebb7523b2018-03-29 08:52:01 +0200151 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
152 f_rtpem_init(vc_RTPEM[i], i);
153 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
154 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200155 if (osmux_on) {
156 f_osmuxem_init(vc_OsmuxEM);
157 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
158 }
Philipp Maier55b90542021-07-02 12:33:19 +0200159
Philipp Maier2ff3e662021-08-19 10:52:33 +0200160 f_init_statsd("VirtCallAgent", vc_STATSD, mp_local_ipv4, mp_mgw_statsd_port);
Philipp Maier55b90542021-07-02 12:33:19 +0200161 connect(self:STATSD_PROC, vc_STATSD:STATSD_PROC);
162
163 f_statsd_reset();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800164 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800165
Philipp Maier3560bd62021-08-19 11:52:23 +0200166 f_ipa_ctrl_start_client(mp_remote_ipv4, mp_mgw_ctrl_port);
167
Harald Welteedc45c12017-11-18 19:15:05 +0100168 if (isvalue(ep)) {
169 /* do a DLCX on all connections of the EP */
170 f_dlcx_ignore(valueof(ep));
171 }
Philipp Maier6137c322019-02-20 16:13:41 +0100172
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200173 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800174 }
175
Harald Welte00a067f2017-09-13 23:27:17 +0200176 testcase TC_selftest() runs on dummy_CT {
177 const charstring c_auep := "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100178 const charstring c_mdcx3 := "MDCX 18983215 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n";
Harald Welte00a067f2017-09-13 23:27:17 +0200179 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
180 "I: 1\n" &
181 "\n" &
182 "v=0\r\n" &
183 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
184 "s=-\r\n" &
185 "c=IN IP4 0.0.0.0\r\n" &
186 "t=0 0\r\n" &
187 "m=audio 0 RTP/AVP 126\r\n" &
188 "a=rtpmap:126 AMR/8000\r\n" &
189 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100190 const charstring c_mdcx4 := "MDCX 18983216 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200191 "M: sendrecv\r" &
192 "C: 2\r\n" &
193 "I: 1\r\n" &
194 "L: p:20, a:AMR, nt:IN\r\n" &
195 "\n" &
196 "v=0\r\n" &
197 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800198 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200199 "c=IN IP4 0.0.0.0\r\n" &
200 "t=0 0\r\n" &
201 "m=audio 4441 RTP/AVP 99\r\n" &
202 "a=rtpmap:99 AMR/8000\r\n" &
203 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800204 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200205
206 log(c_auep);
207 log(dec_MgcpCommand(c_auep));
208
209 log(c_mdcx3);
210 log(dec_MgcpCommand(c_mdcx3));
211
212 log(c_mdcx3_ret);
213 log(dec_MgcpResponse(c_mdcx3_ret));
214
215 log(c_mdcx4);
216 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800217
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100218 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
219 log(enc_MgcpCommand(valueof(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H))));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800220
221 log(c_crcx510_ret);
222 log(dec_MgcpResponse(c_crcx510_ret));
223 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100224
225 /* We didn't encounter any DTE, so pass the test */
226 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800227 }
228
Harald Weltee636afd2017-09-17 16:24:09 +0800229 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100230 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800231 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
232 * - CRCX with remote session description and without
233 *
234 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100235 * x packetization != 20ms
236 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800237 * x unsupported mode (517)
238 * x bidirectional mode before RemoteConnDesc: 527
239 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100240 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800241 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
242 */
243
Harald Welte21ba5572017-09-19 17:55:05 +0800244 /* build a receive template for receiving a MGCP message. You
245 * pass the MGCP response template in, and it will generate an
246 * MGCP_RecvFrom template that can match the primitives arriving on the
247 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800248 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
249 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100250 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200251 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800252 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200253 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800254 locPort := mp_local_udp_port,
255 msg := { response := resp }
256 }
257 return mrf;
258 }
259
260 /* Send a MGCP request + receive a (matching!) response */
261 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
262 var MgcpMessage msg := { command := valueof(cmd) };
263 resp.line.trans_id := cmd.line.trans_id;
264 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800265 var MGCP_RecvFrom mrf;
266 timer T := 5.0;
267
Harald Welte55015362017-11-18 16:02:42 +0100268 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800269 T.start;
270 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800271 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200272 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
273 setverdict(fail, "Response didn't match template");
274 mtc.stop;
275 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800276 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200277 [] T.timeout {
278 setverdict(fail, "Timeout waiting for response to ", cmd);
279 mtc.stop;
280 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800281 }
282 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800283
284 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
285 return mrf.msg.response;
286 } else {
287 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
288 return r;
289 }
Harald Welte00a067f2017-09-13 23:27:17 +0200290 }
291
Harald Welteba62c8c2017-11-18 18:26:49 +0100292 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
293 var integer i;
294 for (i := 0; i < lengthof(resp.params); i := i + 1) {
295 var MgcpParameter par := resp.params[i];
296 if (par.code == "I") {
297 return str2hex(par.val);
298 }
299 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200300 setverdict(fail, "Could not find conn id for MgcpReponse");
301 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100302 return '00000000'H;
303 }
304
Harald Welte10889c12017-11-18 19:40:31 +0100305 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
306 template MgcpCallId call_id := omit,
307 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100308 var template MgcpCommand cmd;
309 var MgcpResponse resp;
310 var template MgcpResponse rtmpl := {
311 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100312 code := ret_code,
313 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100314 },
315 params := *,
316 sdp := *
317 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100318 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100319 resp := mgcp_transceive_mgw(cmd, rtmpl);
320 }
321
Harald Welte10889c12017-11-18 19:40:31 +0100322 /* Send DLCX and expect OK response */
323 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
324 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100325 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100326 }
327
Harald Welteba62c8c2017-11-18 18:26:49 +0100328 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100329 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100330 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100331 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100332 }
333
Harald Weltebb7523b2018-03-29 08:52:01 +0200334 type record HostPort {
335 charstring hostname,
336 integer portnr optional
337 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200338 type record RtpOsmuxFlowData {
339 boolean local_cid_sent, /* whther non wildcarded CID was already sent to MGW */
340 MgcpOsmuxCID local_cid optional,
341 MgcpOsmuxCID remote_cid optional,
342 OsmuxemConfig cfg optional
343 }
Philipp Maierbbe454d2023-03-28 15:31:57 +0200344 type record RtpCodecDescr {
345 uint7_t pt,
346 charstring codec,
347 charstring fmtp optional
348 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200349 type record RtpFlowData {
350 HostPort em, /* emulation side */
351 HostPort mgw, /* mgw side */
Philipp Maier28bb8292018-07-20 17:09:17 +0200352 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierbbe454d2023-03-28 15:31:57 +0200353 record of RtpCodecDescr codec_descr,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100354 RtpemConfig rtp_cfg optional,
Philipp Maierbbe454d2023-03-28 15:31:57 +0200355 RtpOsmuxFlowData osmux
356 }
357
Philipp Maierd6c45592023-03-31 16:41:51 +0200358 template RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
359 charstring codec, template charstring fmtp := omit) := {
360 em := {
361 hostname := host_a,
362 portnr := omit
363 },
364 mgw := {
365 hostname := host_b,
366 portnr := omit
367 },
368 codec_descr := {{
369 pt := pt,
370 codec := codec,
371 fmtp := fmtp
372 }},
373 osmux:= {
374 local_cid_sent := false,
375 local_cid := omit,
376 remote_cid := omit,
377 cfg := omit
378 }
379 }
380
Philipp Maierbbe454d2023-03-28 15:31:57 +0200381 /* To be used with f_flow_create/modify... functions */
382 function f_gen_sdp(RtpFlowData flow) runs on dummy_CT return SDP_Message {
383 var template SDP_Message sdp;
384 var SDP_fmt_list fmt_list := {};
385 var SDP_attribute_list attributes := {};
386
387 /* Add SDP RTMAP attributes (codec type, referenced by PT) */
388 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
389 attributes := attributes & { valueof(ts_SDP_rtpmap(flow.codec_descr[i].pt,
390 flow.codec_descr[i].codec)) };
391 }
392
393 /* Add SDP PTIME attribute, regardless of which codec, the packet intervall remains the same */
394 attributes := attributes & { valueof(ts_SDP_ptime(20)) };
395
396 /* Add SDP FMTP attributes (codec parameters for each codec, referenced by PT) */
397 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
398 if (isvalue(flow.codec_descr[i].fmtp) and flow.codec_descr[i].fmtp != "") {
399 attributes := attributes & { valueof(ts_SDP_fmtp(flow.codec_descr[i].pt,
400 flow.codec_descr[i].fmtp)) };
401 }
402 }
403
404 /* Final step: Generate SDP */
405 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
406 fmt_list := fmt_list & {int2str(flow.codec_descr[i].pt)};
407 }
408 sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42", flow.em.portnr, fmt_list, attributes);
409
410 return valueof(sdp);
411 }
412
413 /* Generate a valid RTP emulation config from the payload type numbers configured in the flow description and
414 * the the default configuration of the RTP emulation module. */
415 function f_gen_rtpem_config_from_flow(RtpFlowData flow) return RtpemConfig {
416 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
417
418 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
419 var RtpemConfigPayload tx_cfg_payload;
420 var RtpemConfigPayload rx_cfg_payload;
421
422 tx_cfg_payload.payload_type := flow.codec_descr[i].pt;
423 tx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.tx_payloads[0].fixed_payload;
424 rx_cfg_payload.payload_type := flow.codec_descr[i].pt;
425 rx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.rx_payloads[0].fixed_payload;
426
427 rtp_cfg.tx_payloads := rtp_cfg.tx_payloads & {tx_cfg_payload};
428 rtp_cfg.rx_payloads := rtp_cfg.rx_payloads & {rx_cfg_payload};
429 }
430
431 return rtp_cfg;
Harald Weltebb7523b2018-03-29 08:52:01 +0200432 }
433
Philipp Maier2321ef92018-06-27 17:52:04 +0200434 /* Create an RTP flow (bidirectional, or receive-only) */
435 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 +0200436 boolean one_phase := true)
437 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200438 var template MgcpCommand cmd;
439 var MgcpResponse resp;
440
441 /* bind local RTP emulation socket */
442 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
443
Philipp Maier28bb8292018-07-20 17:09:17 +0200444 /* configure rtp-emulation */
445 if (ispresent(flow.rtp_cfg)) {
446 f_rtpem_configure(pt, flow.rtp_cfg);
447 } else {
Philipp Maierbbe454d2023-03-28 15:31:57 +0200448 f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow));
Philipp Maier28bb8292018-07-20 17:09:17 +0200449 }
450
Harald Weltebb7523b2018-03-29 08:52:01 +0200451 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200452 /* Connect flow to MGW using a CRCX that also contains an SDP
453 * part that tells the MGW where we are listening for RTP streams
454 * that come from the MGW. We get a fully working connection in
455 * one go. */
456
457 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200458 cmd.sdp := f_gen_sdp(flow);
Philipp Maierc8c0b402019-03-07 10:48:45 +0100459
Harald Weltebb7523b2018-03-29 08:52:01 +0200460 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
461 flow.mgcp_conn_id := extract_conn_id(resp);
462 /* extract port number from response */
463 flow.mgw.portnr :=
464 resp.sdp.media_list[0].media_field.ports.port_number;
465 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200466 /* Create a half-open connection only. We do not tell the MGW
467 * where it can send RTP streams to us. This means this
468 * connection will only be able to receive but can not send
469 * data back to us. In order to turn the connection in a fully
470 * bi-directional one, a separate MDCX is needed. */
471
472 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200473 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
474 flow.mgcp_conn_id := extract_conn_id(resp);
475 /* extract MGW-side port number from response */
476 flow.mgw.portnr :=
477 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200478 }
479 /* finally, connect the emulation-side RTP socket to the MGW */
480 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
481 }
482
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200483 /* Create an Osmux flow (bidirectional, or receive-only) */
484 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
485 boolean one_phase := true)
486 runs on dummy_CT {
487 var template MgcpCommand cmd;
488 var MgcpResponse resp;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200489 var OsmuxTxHandle tx_hdl;
490 var OsmuxRxHandle rx_hdl;
491 var charstring cid_response;
492 var OsmuxCID cid_resp_parsed
493
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200494 /* bind local Osmux emulation socket */
495 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
496
497 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200498 if (ispresent(flow.osmux.cfg)) {
499 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200500 } else {
501 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
502 f_osmuxem_configure(pt, osmux_cfg);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200503 flow.osmux.cfg := osmux_cfg
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200504 }
505
506 if (one_phase) {
507 /* Connect flow to MGW using a CRCX that also contains an SDP
508 * part that tells the MGW where we are listening for Osmux streams
509 * that come from the MGW. We get a fully working connection in
510 * one go. */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200511 if (flow.osmux.local_cid != -1) {
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200512 /* We may still want to negotiate osmux CID later at MDCX */
513 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200514 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200515 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200516 flow.osmux.local_cid_sent := true;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200517 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200518 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200519 cmd.sdp := f_gen_sdp(flow);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200520 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
521 flow.mgcp_conn_id := extract_conn_id(resp);
522 /* extract port number from response */
523 flow.mgw.portnr :=
524 resp.sdp.media_list[0].media_field.ports.port_number;
525 } else {
526 /* Create a half-open connection only. We do not tell the MGW
527 * where it can send Osmux streams to us. This means this
528 * connection will only be able to receive but can not send
529 * data back to us. In order to turn the connection in a fully
530 * bi-directional one, a separate MDCX is needed. */
531
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200532 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200533 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
534
535 flow.mgcp_conn_id := extract_conn_id(resp);
536 /* extract MGW-side port number from response */
537 flow.mgw.portnr :=
538 resp.sdp.media_list[0].media_field.ports.port_number;
539 }
540
541 /* extract Osmux CID we got assigned by the MGW */
542 var MgcpMessage resp_msg := {
543 response := resp
544 }
545
546 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
547 setverdict(fail, "No Osmux CID in MGCP response", resp);
548 mtc.stop;
549 }
550
551 /* Make sure response is no wildcard */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200552 flow.osmux.remote_cid := f_mgcp_osmux_cid_decode(cid_response);
553 if (flow.osmux.remote_cid == -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200554 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
555 mtc.stop;
556 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200557 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux.remote_cid));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200558 f_osmuxem_register_txhandle(pt, tx_hdl);
559
560 /* finally, connect the emulation-side RTP socket to the MGW */
561 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
562 }
563
Philipp Maier2321ef92018-06-27 17:52:04 +0200564 /* Modify an existing RTP flow */
565 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
566 runs on dummy_CT {
567 var template MgcpCommand cmd;
568 var MgcpResponse resp;
569
570 /* rebind local RTP emulation socket to the new address */
571 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
572
Philipp Maier28bb8292018-07-20 17:09:17 +0200573 /* reconfigure rtp-emulation */
574 if (ispresent(flow.rtp_cfg)) {
575 f_rtpem_configure(pt, flow.rtp_cfg);
576 } else {
Philipp Maierbbe454d2023-03-28 15:31:57 +0200577 f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow));
Philipp Maier28bb8292018-07-20 17:09:17 +0200578 }
579
Philipp Maier2321ef92018-06-27 17:52:04 +0200580 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
581 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200582 cmd.sdp := f_gen_sdp(flow);
Philipp Maier2321ef92018-06-27 17:52:04 +0200583 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
584
585 /* extract MGW-side port number from response. (usually this
586 * will not change, but thats is up to the MGW) */
587 flow.mgw.portnr :=
588 resp.sdp.media_list[0].media_field.ports.port_number;
589
590 /* reconnect the emulation-side RTP socket to the MGW */
591 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
592 }
593
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200594 /* Modify an existing Osmux flow */
595 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
596 runs on dummy_CT {
597 var template MgcpCommand cmd;
598 var MgcpResponse resp;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200599 var OsmuxRxHandle rx_hdl;
600 var charstring cid_response;
601 var OsmuxCID cid_resp_parsed
602
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200603 /* rebind local Osmux emulation socket to the new address */
604 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
605
606 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200607 if (ispresent(flow.osmux.cfg)) {
608 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200609 } else {
610 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
611 f_osmuxem_configure(pt, osmux_cfg);
612 }
613
614 /* 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 +0200615 if (flow.osmux.local_cid_sent == false and flow.osmux.local_cid != -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200616 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200617 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200618 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200619 flow.osmux.local_cid_sent := true;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200620 }
621
622 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200623 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 +0200624 cmd.sdp := f_gen_sdp(flow);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200625 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
626
627 /* extract MGW-side port number from response. (usually this
628 * will not change, but thats is up to the MGW) */
629 flow.mgw.portnr :=
630 resp.sdp.media_list[0].media_field.ports.port_number;
631
632 /* extract Osmux CID we got assigned by the MGW */
633 var MgcpMessage resp_msg := {
634 response := resp
635 }
636
637 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
638 setverdict(fail, "No Osmux CID in MGCP response", resp);
639 mtc.stop;
640 }
641
642 /* Make sure response is no wildcard */
643 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
644 if (cid_resp_parsed == -1) {
645 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
646 mtc.stop;
647 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200648 if (cid_resp_parsed != flow.osmux.remote_cid) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200649 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
650 mtc.stop;
651 }
652
653 /* reconnect the emulation-side Osmux socket to the MGW */
654 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
655 }
656
Philipp Maier2321ef92018-06-27 17:52:04 +0200657 /* Delete an existing RTP flow */
658 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
659 runs on dummy_CT {
660 var template MgcpCommand cmd;
661 var MgcpResponse resp;
662
663 /* Switch off RTP flow */
664 f_rtpem_mode(pt, RTPEM_MODE_NONE);
665
666 /* Delete connection on MGW (if needed) */
667 if (isvalue(call_id) and isvalue(ep)) {
668 f_sleep(0.1);
669 f_dlcx_ok(valueof(ep), call_id);
670 }
671 }
672
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200673 /* Delete an existing Osmux flow */
674 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
675 runs on dummy_CT {
676 var template MgcpCommand cmd;
677 var MgcpResponse resp;
678
679 /* Switch off Osmux flow */
680 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
681
682 /* Delete connection on MGW (if needed) */
683 if (isvalue(call_id) and isvalue(ep)) {
684 f_sleep(0.1);
685 f_dlcx_ok(valueof(ep), call_id);
686 }
687 }
688
Pau Espin Pedrolb26d4462023-06-15 11:38:02 +0200689 /* Send an AuditEndpoint message to the MGW */
690 function f_auep(charstring ep_prefix) runs on dummy_CT {
691 var MgcpEndpoint ep := ep_prefix & "@" & c_mgw_domain;
692 var template MgcpCommand cmd;
693 var MgcpResponse resp;
694
695 f_init(ep);
696
697 cmd := ts_AUEP(get_next_trans_id(), ep);
698 resp := mgcp_transceive_mgw(cmd, tr_AUEP_ACK);
699 }
700
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100701 function f_crcx(charstring ep_prefix) runs on dummy_CT {
702 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800703 var template MgcpCommand cmd;
704 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100705 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800706
Harald Welteedc45c12017-11-18 19:15:05 +0100707 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800708
Harald Welteba62c8c2017-11-18 18:26:49 +0100709 /* create the connection on the MGW */
710 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100711 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100712 extract_conn_id(resp);
713
714 /* clean-up */
715 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100716 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100717
Philipp Maier45635f42018-06-05 17:28:02 +0200718 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
719 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
720 var template MgcpCommand cmd;
721 var MgcpResponse resp;
722 var MgcpCallId call_id := '1234'H;
723
724 f_init(ep);
725
726 /* create the connection on the MGW */
727 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
728 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
729 extract_conn_id(resp);
730
731 /* clean-up */
732 f_dlcx_ok(ep, call_id);
733
734 /* See also OS#2658: Even when we omit the LCO information, we
735 expect the MGW to pick a sane payload type for us. This
736 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200737 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200738 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200739 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200740 }
741
742 /* See also OS#2658: We also expect the MGW to assign a port
743 number to us. */
744 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
745 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200746 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200747 }
748 }
749
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200750 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
751 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
752 var template MgcpCommand cmd;
753 var MgcpResponse resp;
754 var MgcpCallId call_id := '1234'H;
755 var charstring cid_response;
756
757 if (run_init) {
758 f_init(ep, true);
759 }
760
761 /* create the connection on the MGW */
762 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
763 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
764 extract_conn_id(resp);
765
766 /* extract Osmux CID we got assigned by the MGW */
767 var MgcpMessage resp_msg := {
768 response := resp
769 }
770
771 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
772 setverdict(fail, "No Osmux CID in MGCP response", resp);
773 mtc.stop;
774 }
775
776 /* Make sure response is no wildcard */
777 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
778 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
779 mtc.stop;
780 }
781
782 /* clean-up */
783 f_dlcx_ok(ep, call_id);
784 }
785
Pau Espin Pedrolb26d4462023-06-15 11:38:02 +0200786 /* test valid AUEP towards "null" endpoint */
787 testcase TC_auep_null() runs on dummy_CT {
788 f_auep("null");
789 setverdict(pass);
790 }
791
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100792 /* test valid CRCX without SDP */
793 testcase TC_crcx() runs on dummy_CT {
794 f_crcx(c_mgw_ep_rtpbridge);
795 setverdict(pass);
796 }
797
Philipp Maier45635f42018-06-05 17:28:02 +0200798 /* test valid CRCX without SDP and LCO */
799 testcase TC_crcx_no_lco() runs on dummy_CT {
800 f_crcx_no_lco(c_mgw_ep_rtpbridge);
801 setverdict(pass);
802 }
803
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100804 /* test valid CRCX without SDP (older method without endpoint prefix) */
805 testcase TC_crcx_noprefix() runs on dummy_CT {
806 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800807 setverdict(pass);
808 }
809
810 /* test CRCX with unsupported mode, expect 517 */
811 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
812 var template MgcpCommand cmd;
813 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100814 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100815 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800816 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
817
Harald Welteedc45c12017-11-18 19:15:05 +0100818 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800819
Harald Welteba62c8c2017-11-18 18:26:49 +0100820 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800821 resp := mgcp_transceive_mgw(cmd, rtmpl);
822 setverdict(pass);
823 }
824
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200825 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
826 testcase TC_crcx_osmo_ign() runs on dummy_CT {
827 var template MgcpCommand cmd;
828 var MgcpResponse resp;
829 var MgcpEndpoint ep := "7@" & c_mgw_domain;
830 var MgcpCallId call_id := '3'H;
831
832 f_init(ep);
833
834 /* CRCX 1 7@mgw MGCP 1.0
835 C: 3
836 L: p:20, a:GSM-EFR, nt:IN
837 M: recvonly
838 X-Osmo-IGN: C
839 */
840
841 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
842 cmd.params := {ts_MgcpParCallId(call_id),
843 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
844 t_MgcpParConnMode("recvonly"),
845 t_MgcpParOsmoIGN("C")};
846 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
847 extract_conn_id(resp);
848
849 /* clean-up */
850 f_dlcx_ok(ep, call_id);
851 setverdict(pass);
852 }
853
Harald Welte21ba5572017-09-19 17:55:05 +0800854 /* test CRCX with early bi-directional mode, expect 527 as
855 * bi-diretional media can only be established once both local and
856 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800857 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
858 var template MgcpCommand cmd;
859 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100860 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100861 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800862 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
863
Harald Welteedc45c12017-11-18 19:15:05 +0100864 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800865
Harald Welteba62c8c2017-11-18 18:26:49 +0100866 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800867 resp := mgcp_transceive_mgw(cmd, rtmpl);
868 setverdict(pass);
869 }
870
871 /* test CRCX with unsupported Parameters */
872 testcase TC_crcx_unsupp_param() runs on dummy_CT {
873 var template MgcpCommand cmd;
874 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100875 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100876 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800877 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
878
Harald Welteedc45c12017-11-18 19:15:05 +0100879 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800880
Harald Welteba62c8c2017-11-18 18:26:49 +0100881 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100882 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
883 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
884
Harald Weltee636afd2017-09-17 16:24:09 +0800885 resp := mgcp_transceive_mgw(cmd, rtmpl);
886 setverdict(pass);
887 }
888
889 /* test CRCX with missing CallId */
890 testcase TC_crcx_missing_callid() runs on dummy_CT {
891 var template MgcpCommand cmd;
892 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100893 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100894 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800895
Harald Welteedc45c12017-11-18 19:15:05 +0100896 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800897
Harald Welteba62c8c2017-11-18 18:26:49 +0100898 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800899 cmd.params := {
900 t_MgcpParConnMode("recvonly"),
901 t_MgcpParLocConnOpt("p:20")
902 }
903 resp := mgcp_transceive_mgw(cmd, rtmpl);
904 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100905
Harald Weltee636afd2017-09-17 16:24:09 +0800906 }
907
908 /* test CRCX with missing Mode */
909 testcase TC_crcx_missing_mode() runs on dummy_CT {
910 var template MgcpCommand cmd;
911 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100912 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100913 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100914 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800915
Harald Welteedc45c12017-11-18 19:15:05 +0100916 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800917
Harald Welteba62c8c2017-11-18 18:26:49 +0100918 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800919 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100920 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800921 t_MgcpParLocConnOpt("p:20")
922 }
923 resp := mgcp_transceive_mgw(cmd, rtmpl);
924 setverdict(pass);
925 }
926
927 /* test CRCX with unsupported packetization interval */
928 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
929 var template MgcpCommand cmd;
930 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100931 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100932 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100933 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800934
Harald Welteedc45c12017-11-18 19:15:05 +0100935 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800936
Harald Welteba62c8c2017-11-18 18:26:49 +0100937 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100938 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800939 resp := mgcp_transceive_mgw(cmd, rtmpl);
940 setverdict(pass);
941 }
942
943 /* test CRCX with illegal double presence of local connection option */
944 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
945 var template MgcpCommand cmd;
946 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100947 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100948 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800949 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
950
Harald Welteedc45c12017-11-18 19:15:05 +0100951 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800952
Harald Welteba62c8c2017-11-18 18:26:49 +0100953 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100954 /* p:20 is permitted only once and not twice! */
955 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800956 resp := mgcp_transceive_mgw(cmd, rtmpl);
957 setverdict(pass);
958 }
959
960 /* test valid CRCX with valid SDP */
961 testcase TC_crcx_sdp() runs on dummy_CT {
962 var template MgcpCommand cmd;
963 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100964 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100965 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800966
Harald Welteedc45c12017-11-18 19:15:05 +0100967 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800968
Harald Welteba62c8c2017-11-18 18:26:49 +0100969 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800970 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
971 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
972 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100973 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100974
975 /* clean-up */
976 f_dlcx_ok(ep, call_id);
977
Harald Weltee636afd2017-09-17 16:24:09 +0800978 setverdict(pass);
979 }
980
Philipp Maier5e06cee2018-02-01 18:28:08 +0100981 /* test valid wildcarded CRCX */
982 testcase TC_crcx_wildcarded() runs on dummy_CT {
983 var template MgcpCommand cmd;
984 var MgcpResponse resp;
985 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
986 var MgcpCallId call_id := '1234'H;
987 var MgcpEndpoint ep_assigned;
988 f_init();
989
990 /* create the connection on the MGW */
991 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
992 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
993 extract_conn_id(resp);
994
995 /* extract endpoint name we got assigned by the MGW */
996 var MgcpMessage resp_msg := {
997 response := resp
998 }
999 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
1000 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001001 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +01001002 }
1003
1004 /* clean-up */
1005 f_dlcx_ok(ep_assigned, call_id);
1006
1007 setverdict(pass);
1008 }
1009
1010 /* test valid wildcarded CRCX */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001011 type record of MgcpEndpoint MgcpEndpointList;
Philipp Maier5e06cee2018-02-01 18:28:08 +01001012 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maier5e06cee2018-02-01 18:28:08 +01001013 var integer i;
1014 var template MgcpCommand cmd;
1015 var MgcpResponse resp;
1016 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1017 var MgcpCallId call_id := '1234'H;
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001018 var MgcpEndpoint ep_assigned;
1019 var MgcpEndpointList ep_assigned_li := {};
Philipp Maier5e06cee2018-02-01 18:28:08 +01001020 f_init();
1021
1022 /* Exhaust all endpoint resources on the virtual trunk */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001023 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier5e06cee2018-02-01 18:28:08 +01001024 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1025 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1026
1027 /* Make sure we got a connection id */
1028 extract_conn_id(resp);
1029
1030 var MgcpMessage resp_msg := {
1031 response := resp
1032 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001033 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
Philipp Maier5e06cee2018-02-01 18:28:08 +01001034 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001035 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +01001036 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001037 ep_assigned_li := ep_assigned_li & {ep_assigned}
Philipp Maier5e06cee2018-02-01 18:28:08 +01001038 }
1039
1040 /* Try to allocate one more endpoint, which should fail */
1041 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1042 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
1043 resp := mgcp_transceive_mgw(cmd, rtmpl);
1044 setverdict(pass);
1045
1046 /* clean-up */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001047 for (i := 0; i < mp_num_endpoints; i := i+1) {
1048 f_dlcx_ok(ep_assigned_li[i], call_id);
Philipp Maier5e06cee2018-02-01 18:28:08 +01001049 }
1050 setverdict(pass);
1051 }
1052
Harald Weltee636afd2017-09-17 16:24:09 +08001053 /* TODO: various SDP related bits */
1054
1055
1056 /* TODO: CRCX with X-Osmux */
1057 /* TODO: double CRCX without force_realloc */
1058
1059 /* TODO: MDCX (various) */
1060
1061 /* TODO: MDCX without CRCX first */
1062 testcase TC_mdcx_without_crcx() runs on dummy_CT {
1063 var template MgcpCommand cmd;
1064 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001065 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +01001066 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +08001067 var template MgcpResponse rtmpl := {
1068 line := {
1069 /* TODO: accept/enforce better error? */
1070 code := "400",
1071 string := ?
1072 },
1073 params:= { },
1074 sdp := omit
1075 };
1076
Harald Welteedc45c12017-11-18 19:15:05 +01001077 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001078
Harald Welte2bcfd3a2017-11-18 22:14:35 +01001079 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001080 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1081 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1082 valueof(ts_SDP_ptime(20)) });
1083 resp := mgcp_transceive_mgw(cmd, rtmpl);
1084 setverdict(pass);
1085 }
1086
1087 /* DLCX without CRCX first */
1088 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1089 var template MgcpCommand cmd;
1090 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001091 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001092 var template MgcpResponse rtmpl := {
1093 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001094 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001095 string := ?
1096 },
1097 params:= { },
1098 sdp := omit
1099 };
1100
Harald Welteedc45c12017-11-18 19:15:05 +01001101 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001102
Harald Welteedc45c12017-11-18 19:15:05 +01001103 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001104 resp := mgcp_transceive_mgw(cmd, rtmpl);
1105 setverdict(pass);
1106 }
1107
Philipp Maier21c1cff2021-07-20 14:22:53 +02001108 /* DLCX to non existing endpoint */
1109 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1110 var template MgcpCommand cmd;
1111 var MgcpResponse resp;
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001112 var charstring non_existant_ep := hex2str(int2hex(mp_num_endpoints + 1, 4))
1113 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & non_existant_ep & "@" & c_mgw_domain;
Philipp Maier21c1cff2021-07-20 14:22:53 +02001114 var template MgcpResponse rtmpl := {
1115 line := {
1116 code := ("500"),
1117 string := ?
1118 },
1119 params:= { },
1120 sdp := omit
1121 };
1122
1123 f_init(ep);
1124
1125 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1126 resp := mgcp_transceive_mgw(cmd, rtmpl);
1127 setverdict(pass);
1128 }
1129
Philipp Maier8a3dc922018-02-02 14:55:12 +01001130 /* test valid wildcarded MDCX */
1131 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1132 /* Note: A wildcarded MDCX is not allowed, so we expect the
1133 * MGW to reject this request */
1134 var template MgcpCommand cmd;
1135 var MgcpResponse resp;
1136 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1137 var MgcpCallId call_id := '1225'H;
1138 var template MgcpResponse rtmpl := {
1139 line := {
1140 /* TODO: accept/enforce better error? */
1141 code := "507",
1142 string := ?
1143 },
1144 params:= { },
1145 sdp := omit
1146 };
1147
1148 f_init(ep);
1149
1150 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1151 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1152 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1153 valueof(ts_SDP_ptime(20)) });
1154 resp := mgcp_transceive_mgw(cmd, rtmpl);
1155 setverdict(pass);
1156 }
1157
1158 /* test valid wildcarded DLCX */
1159 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001160 var template MgcpCommand cmd;
1161 var MgcpResponse resp;
1162 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001163 var integer i;
1164 var MgcpCallId call_id := '1234'H;
1165 var StatsDExpects expect;
1166 f_init(ep);
1167
1168 /* Allocate a few endpoints */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001169 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier55b90542021-07-02 12:33:19 +02001170 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1171 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1172 }
1173
Philipp Maier1298b092021-11-18 18:33:39 +01001174 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
1175 * occupied endpoints */
1176 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001177 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001178 { 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 +02001179 };
1180 f_statsd_expect(expect);
1181
1182 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001183 var template MgcpResponse rtmpl := {
1184 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001185 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001186 string := ?
1187 },
1188 params:= { },
1189 sdp := omit
1190 };
Philipp Maier55b90542021-07-02 12:33:19 +02001191 cmd := ts_DLCX(get_next_trans_id(), ep);
1192 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001193
Philipp Maier6c740e82022-06-30 12:04:34 +02001194 /* Query a the statsd once to ensure that intermediate results are pulled from the
1195 * pipeline. The second query (below) will return the actual result. */
1196 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001197 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := mp_num_endpoints}
Philipp Maier6c740e82022-06-30 12:04:34 +02001198 };
1199 f_statsd_expect(expect);
1200
1201 /* The second query must resturn a result with 0 endpoints in use. */
Philipp Maier55b90542021-07-02 12:33:19 +02001202 expect := {
1203 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1204 };
1205 f_statsd_expect(expect);
1206
Philipp Maier8a3dc922018-02-02 14:55:12 +01001207 setverdict(pass);
1208 }
1209
Harald Welte79181ff2017-11-18 19:26:11 +01001210 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1211 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1212 var template MgcpCommand cmd;
1213 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001214 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001215 var MgcpCallId call_id := '51234'H;
1216
1217 f_init(ep);
1218
1219 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1220 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1221
1222 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1223
1224 setverdict(pass);
1225 }
1226
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001227 /* test valid CRCX without SDP */
1228 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1229 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1230 setverdict(pass);
1231 }
1232
1233 /* test valid CRCX without SDP */
1234 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1235 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1236 setverdict(pass);
1237 }
1238
1239 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1240 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1241 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1242 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1243 setverdict(pass);
1244 }
1245
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02001246 /* test Creating 257 concurrent osmux conns. It should fail since maximum is 256. */
1247 testcase TC_crcx_osmux_257() runs on dummy_CT {
1248 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1249 var template MgcpCommand cmd;
1250 var MgcpResponse resp;
1251 var charstring cid_response;
1252 var integer i;
1253
1254 f_init(ep, true);
1255
1256 for (i := 0; i < 256; i := i + 1) {
1257
1258 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1259 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
1260 extract_conn_id(resp);
1261
1262 /* extract Osmux CID we got assigned by the MGW */
1263 var MgcpMessage resp_msg := {
1264 response := resp
1265 }
1266
1267 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
1268 setverdict(fail, "No Osmux CID in MGCP response", resp);
1269 mtc.stop;
1270 }
1271
1272 /* Make sure response is no wildcard */
1273 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
1274 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
1275 mtc.stop;
1276 }
1277 }
1278
1279 /* Now conn num 257, it should fail due to all Osmux conns already allocated: */
1280 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1281 resp := mgcp_transceive_mgw(cmd, tr_MgcpResp_Err("400"));
1282
1283 setverdict(pass);
1284
1285 /* Clean up */
1286 for (i := 0; i < 256; i := i + 1) {
1287 f_dlcx_ok(ep, int2hex(i, 4));
1288 }
1289 }
1290
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001291 /* Create one half open connection in receive-only mode. The MGW must accept
1292 * the packets but must not send any. */
1293 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1294 var RtpFlowData flow;
1295 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1296 var MgcpCallId call_id := '1225'H;
1297 var OsmuxemStats stats;
1298 var OsmuxTxHandle tx_hdl;
1299
1300 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001301 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001302 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001303 flow.osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001304 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1305
1306 /* create a transmitter not yet known by MGW */
1307 tx_hdl := valueof(t_TxHandleAMR590(2));
1308 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1309
1310 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1311 f_sleep(1.0);
1312 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1313
1314 stats := f_osmuxem_stats_get(OsmuxEM);
1315
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001316 if (stats.num_pkts_tx < 40 / flow.osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001317 setverdict(fail);
1318 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001319 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 +02001320 setverdict(fail);
1321 }
1322
1323 f_osmuxem_stats_err_check(stats);
1324
1325 setverdict(pass);
1326 }
1327
1328 /* Create one connection in loopback mode, test if the Osmux packets are
1329 * actually reflected */
1330 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1331 var RtpFlowData flow;
1332 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1333 var MgcpCallId call_id := '1225'H;
1334 var OsmuxemStats stats;
1335 var OsmuxTxHandle tx_hdl;
1336
1337 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001338 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001339 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001340 flow.osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001341 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1342
1343 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1344 f_sleep(1.0);
1345
1346 /* Switch off both Tx, wait to receive delayed frames from MGW */
1347 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1348 f_sleep(0.1);
1349 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1350
1351 stats := f_osmuxem_stats_get(OsmuxEM);
1352
1353 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1354 setverdict(fail);
1355 }
1356 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1357 setverdict(fail);
1358 }
1359
1360 f_osmuxem_stats_err_check(stats);
1361
1362 setverdict(pass);
1363 }
1364
1365 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1366 * must match the reception statistics on the other side and vice versa. The
1367 * user may also supply a tolerance value (number of packets) when deviations
1368 * are acceptable */
1369 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1370 var integer plen;
1371
1372 log("stats A: ", a);
1373 log("stats B: ", b);
1374 log("tolerance: ", tolerance, " packets");
1375 log("batch_size: ", batch_size, " packets");
1376
1377 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1378
1379 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1380 return false;
1381 }
1382
1383 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1384 return false;
1385 }
1386
1387 if(a.num_pkts_tx > 0) {
1388 plen := a.bytes_payload_tx / a.num_pkts_tx;
1389 } else {
1390 plen := 0;
1391 }
1392
1393 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1394 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1395 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1396 return false;
1397 }
1398
1399 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) {
1400 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1401 return false;
1402 }
1403
1404 return true;
1405 }
1406
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001407 function f_TC_two_crcx_and_rtp_osmux(boolean bidir, boolean rtp_amr_oa,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001408 charstring local_ip_rtp, charstring remote_ip_rtp,
1409 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001410 var RtpFlowData flow[2];
1411 var RtpemStats stats_rtp;
1412 var OsmuxemStats stats_osmux;
1413 var MgcpResponse resp;
1414 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1415 var MgcpCallId call_id := '1226'H;
1416 var integer tolerance := 0;
1417
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001418 var octetstring amr_payload;
1419 var charstring fmtp;
1420
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001421 f_init(ep, true);
1422
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001423 var AMRFT cmr := AMR_FT_0;
1424 var AMRFT ft := AMR_FT_2;
1425 if (rtp_amr_oa) {
1426 fmtp := "octet-align=1";
1427 var RTP_AMR_Hdr amr_oa_hdr := valueof(ts_RTP_AMR_Hdr(enum2int(cmr), enum2int(ft)));
1428 amr_payload := enc_RTP_AMR_Hdr(amr_oa_hdr) &
1429 f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload);
1430 } else {
1431 fmtp := "octet-align=0";
1432 /* Convert OA to BWE: */
1433 var RTP_AMR_BWE_Hdr amr_bwe_hdr := valueof(ts_RTP_AMR_BWE_Hdr(enum2int(cmr), enum2int(ft)));
1434 var bitstring amr_bwe_hdr_bits := substr(oct2bit(enc_RTP_AMR_BWE_Hdr(amr_bwe_hdr)), 0 , 10);
1435 var bitstring amr_data_bits := oct2bit(f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload));
1436 var bitstring amr_payload_bits := amr_bwe_hdr_bits & substr(amr_data_bits, 0, f_amrft_payload_bits_len(enum2int(ft)));
1437 amr_payload := bit2oct(f_pad_bit(amr_payload_bits, (lengthof(amr_payload_bits)+7)/8*8, '0'B));
1438 };
1439
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001440 /* from us to MGW */
Philipp Maier6b41e152023-03-31 11:45:24 +02001441 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000", fmtp));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001442 flow[0].rtp_cfg := c_RtpemDefaultCfg
Philipp Maierbbe454d2023-03-28 15:31:57 +02001443 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1444 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := amr_payload;
1445 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1446 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := amr_payload;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001447 /* bind local RTP emulation sockets */
1448 flow[0].em.portnr := 10000;
1449 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1450
1451 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001452 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001453 flow[1].em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001454 flow[1].osmux.local_cid := 2;
1455 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001456 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1457
1458 if (bidir) {
1459 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1460 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1461
1462 /* Note: When we test bidirectional we may
1463 * loose packets during switch off because
1464 * both ends are transmitting and we only
1465 * can switch them off one by one. */
1466 tolerance := 3;
1467 } else {
1468 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1469 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1470 }
1471
1472 f_sleep(1.0);
1473
1474 /* Switch off both Tx, wait to receive delayed frames from MGW */
1475 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1476 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1477 f_sleep(0.1);
1478
1479 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1480 f_flow_delete(RTPEM[1]);
1481
1482 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1483 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001484 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 +02001485 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1486 mtc.stop;
1487 }
1488
1489 f_rtpem_stats_err_check(stats_rtp);
1490 f_osmuxem_stats_err_check(stats_osmux);
1491
1492 setverdict(pass);
1493 }
1494
1495 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1496 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001497 f_TC_two_crcx_and_rtp_osmux(false, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001498 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001499 }
1500
1501 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1502 * exchange some data in both directions */
1503 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001504 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001505 mp_local_ipv4, mp_remote_ipv4);
1506 }
1507
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001508 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1509 * exchange some data in both directions. RTP side is configured to
1510 * rx/rx AMR in bandwidth-efficient mode. */
1511 testcase TC_two_crcx_and_rtp_osmux_bidir_amr_bwe() runs on dummy_CT {
1512 f_TC_two_crcx_and_rtp_osmux(true, false, mp_local_ipv4, mp_remote_ipv4,
1513 mp_local_ipv4, mp_remote_ipv4);
1514 }
1515
1516
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001517 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1518 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001519 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001520 mp_local_ipv6, mp_remote_ipv6);
1521 }
1522 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1523 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001524 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001525 mp_local_ipv6, mp_remote_ipv6);
1526 }
1527 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1528 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001529 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001530 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001531 }
1532
1533
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001534 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1535 charstring local_ip_rtp, charstring remote_ip_rtp,
1536 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001537 var RtpFlowData flow[2];
1538 var RtpemStats stats_rtp;
1539 var OsmuxemStats stats_osmux;
1540 var MgcpResponse resp;
1541 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1542 var MgcpCallId call_id := '1227'H;
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001543 var integer num_pkts_tx[2], num_pkts_rx[2];
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001544 var integer temp;
1545
1546 f_init(ep, true);
1547
1548 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001549 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001550 flow[0].rtp_cfg := c_RtpemDefaultCfg
Philipp Maierbbe454d2023-03-28 15:31:57 +02001551 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1552 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001553 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
Philipp Maierbbe454d2023-03-28 15:31:57 +02001554 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);
1555 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 +02001556 /* bind local RTP emulation sockets */
1557 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001558 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001559
1560
1561 /* Create the second connection. This connection will be also
1562 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001563 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001564 flow[1].em.portnr := mp_local_osmux_port;
1565 if (crcx_osmux_wildcard) {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001566 flow[1].osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001567 } else {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001568 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001569 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001570 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001571 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001572
1573
1574 /* The first leg starts transmitting */
1575 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1576 f_sleep(0.5);
1577 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1578 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1579 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1580 mtc.stop;
1581 }
1582 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1583 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1584 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1585 mtc.stop;
1586 }
1587
1588 /* The second leg starts transmitting a little later */
1589 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1590 f_sleep(1.0);
1591 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1592 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1593 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1594 mtc.stop;
1595 }
1596 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1597 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1598 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1599 mtc.stop;
1600 }
1601
1602 /* The first leg will now be switched into bidirectional
1603 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001604 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001605 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001606 /* At this point in time, flow[1](Osmux)->MGW->flow[0](RTP) is active,
1607 * hence if local CID was provided during CRCX we should already be seeing packets
1608 * flowing in one direction, aka stats_rtp.num_pkts_rx sould be >0 after a while: */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001609 f_sleep(0.5);
1610 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1611 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1612 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1613 mtc.stop;
1614 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001615 if (not crcx_osmux_wildcard and stats_rtp.num_pkts_rx == 0) {
1616 setverdict(fail, "received 0 packets Osmux->MGW->RTP");
1617 mtc.stop;
1618 }
1619
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001620 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1621 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1622 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1623 mtc.stop;
1624 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001625 if (stats_osmux.num_pkts_rx > 0) {
1626 setverdict(fail, "received unexpected ", stats_osmux.num_pkts_rx, " packets RTP->MGW->Osmux");
1627 mtc.stop;
1628 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001629
1630 /* When the second leg is switched into bidirectional mode
1631 * as well, then the MGW will connect the two together and
1632 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001633 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001634 if (crcx_osmux_wildcard) {
Pau Espin Pedrol982b9792022-10-07 11:48:13 +02001635 /* We set now the local CID in MDCX: */
1636 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001637 }
1638 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001639 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1640 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1641 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
1642 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
1643 num_pkts_rx[0] := stats_rtp.num_pkts_rx;
1644 num_pkts_rx[1] := stats_osmux.num_pkts_rx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001645 f_sleep(2.0);
1646
1647 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1648 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1649
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001650 temp := (stats_rtp.num_pkts_tx - num_pkts_tx[0]) -
1651 (stats_osmux.num_pkts_rx - num_pkts_rx[1]) * flow[1].osmux.cfg.batch_size;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001652 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 +02001653 log("stats_rtp: ", stats_rtp);
1654 log("stats_osmux: ", stats_osmux);
1655 log("old_rtp_tx: ", num_pkts_tx[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001656 setverdict(fail, "RTP-Tx vs OSmux-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001657 mtc.stop;
1658 }
1659
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001660 temp := (stats_osmux.num_pkts_tx - num_pkts_tx[1]) -
1661 ((stats_rtp.num_pkts_rx - num_pkts_rx[0])/ flow[1].osmux.cfg.batch_size);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001662 if (temp > 3 or temp < -3) {
Pau Espin Pedroldb1e0682022-10-06 19:40:29 +02001663 log("stats_rtp: ", stats_rtp);
1664 log("stats_osmux: ", stats_osmux);
1665 log("old_osmux_tx: ", num_pkts_tx[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001666 setverdict(fail, "Osmux-Tx vs RTP-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001667 mtc.stop;
1668 }
1669
1670 f_rtpem_stats_err_check(stats_rtp);
1671 f_osmuxem_stats_err_check(stats_osmux);
1672
1673 /* Tear down */
1674 f_flow_delete(RTPEM[0]);
1675 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1676 setverdict(pass);
1677 }
1678
1679 /* create one RTP and one OSmux emulations and pass data in both
1680 directions. Create CRCX with wildcard Osmux CID and set it later
1681 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1682 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001683 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1684 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001685 }
1686
1687 /* create one RTP and one OSmux emulations and pass data in both
1688 directions. Create CRCX with fixed Osmux CID and keep it during
1689 MDCX. This is similar to how BSC sets up the call in AoIP. */
1690 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001691 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1692 mp_local_ipv4, mp_remote_ipv4);
1693 }
1694
1695 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1696 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1697 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1698 mp_local_ipv6, mp_remote_ipv6);
1699 }
1700 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1701 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1702 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1703 mp_local_ipv6, mp_remote_ipv6);
1704 }
1705 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1706 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1707 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1708 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001709 }
1710
Harald Welte646ecdb2017-12-28 03:21:57 +01001711 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1712 var template MgcpCommand cmd;
1713 var MgcpResponse resp;
1714
1715 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1716 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1717
1718 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1719
1720 setverdict(pass);
1721 }
1722
1723 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1724 var MgcpEndpoint ep;
1725 var MgcpCallId call_id;
1726 var integer ep_nr;
1727
1728 f_init();
1729
1730 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001731 if(ep_nr > 15) {
1732 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1733 } else {
1734 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1735 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001736 call_id := int2hex(ep_nr, 2) & '1234'H;
1737 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1738 }
1739 }
1740
Harald Welte79181ff2017-11-18 19:26:11 +01001741 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1742 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001743 var template MgcpCommand cmd;
1744 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001745 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001746 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001747
Harald Welteedc45c12017-11-18 19:15:05 +01001748 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001749
Harald Welteba62c8c2017-11-18 18:26:49 +01001750 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001751 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001752
Harald Welteba62c8c2017-11-18 18:26:49 +01001753 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001754
1755 setverdict(pass);
1756 }
1757
Harald Welte79181ff2017-11-18 19:26:11 +01001758 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1759 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1760 var template MgcpCommand cmd;
1761 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001762 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001763 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001764
1765 f_init(ep);
1766
1767 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1768 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1769
1770 f_dlcx_ok(ep);
1771
1772 setverdict(pass);
1773 }
1774
1775
Harald Welte6d167f82017-11-18 19:41:35 +01001776 /* CRCX + DLCX of valid endpoint but invalid call-id */
1777 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1778 var template MgcpCommand cmd;
1779 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001780 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001781 var MgcpCallId call_id := '51231'H;
1782
1783 f_init(ep);
1784
1785 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1786 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1787
1788 f_dlcx(ep, "516", *, 'ffff'H);
1789
1790 setverdict(pass);
1791 }
1792
1793
1794 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1795 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1796 var template MgcpCommand cmd;
1797 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001798 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001799 var MgcpCallId call_id := '51230'H;
1800
1801 f_init(ep);
1802
1803 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1804 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1805
1806 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1807
1808 setverdict(pass);
1809 }
1810
1811
Harald Weltee636afd2017-09-17 16:24:09 +08001812 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001813 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1814 var template MgcpCommand cmd;
1815 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001816 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001817 var MgcpCallId call_id := '51229'H;
1818 var template MgcpResponse rtmpl := {
1819 line := {
1820 code := "200",
1821 string := "OK"
1822 },
1823 params:= { },
1824 sdp := omit
1825 };
1826
1827 f_init(ep);
1828
1829 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1830 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1831
1832 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1833 resp := mgcp_transceive_mgw(cmd, rtmpl);
1834 resp := mgcp_transceive_mgw(cmd, rtmpl);
1835
1836 setverdict(pass);
1837 }
1838
Harald Weltebb7523b2018-03-29 08:52:01 +02001839 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1840 testcase TC_rtpem_selftest() runs on dummy_CT {
1841 var RtpemStats stats[2];
1842 var integer local_port := 10000;
1843 var integer local_port2 := 20000;
1844
1845 f_init();
1846
1847 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1848 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1849
1850 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1851 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1852
1853 log("=== starting");
1854 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1855 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1856
1857 f_sleep(5.0);
1858
1859 log("=== stopping");
1860 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1861 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1862 f_sleep(0.5);
1863 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1864 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1865
1866 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1867 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1868 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1869 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001870 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001871 }
1872 setverdict(pass);
1873 }
1874
Philipp Maier2321ef92018-06-27 17:52:04 +02001875 /* Create one half open connection in receive-only mode. The MGW must accept
1876 * the packets but must not send any. */
1877 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1878 var RtpFlowData flow;
1879 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1880 var MgcpCallId call_id := '1225'H;
1881 var RtpemStats stats;
1882
1883 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001884 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001885 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001886 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001887
1888 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1889 f_sleep(1.0);
1890 f_flow_delete(RTPEM[0], ep, call_id);
1891
1892 stats := f_rtpem_stats_get(RTPEM[0]);
1893
Philipp Maier42b17cc2019-10-01 13:53:17 +02001894 /* Make sure that at least some amount of RTP packets/bytes
1895 * have has been transmitted. The compare values for
1896 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1897 * using a testrun and the results were devided by 2, so even
1898 * in load situations we should reach the minimum amount of
1899 * required packets/bytes */
1900
1901 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001902 setverdict(fail);
1903 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001904 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001905 setverdict(fail);
1906 }
Philipp Maier36291392018-07-25 09:40:44 +02001907
1908 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001909
1910 setverdict(pass);
1911 }
1912
1913 /* Create one connection in loopback mode, test if the RTP packets are
1914 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001915 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 +02001916 var RtpFlowData flow;
1917 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1918 var MgcpCallId call_id := '1225'H;
1919 var RtpemStats stats;
1920
1921 f_init(ep);
Philipp Maier47ec8122023-05-23 11:38:30 +02001922 flow := valueof(t_RtpFlow(local_ip, remote_ip, 112, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001923 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001924 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001925
1926 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1927 f_sleep(1.0);
1928 f_flow_delete(RTPEM[0], ep, call_id);
1929
1930 stats := f_rtpem_stats_get(RTPEM[0]);
1931
1932 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1933 setverdict(fail);
1934 }
1935 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1936 setverdict(fail);
1937 }
Philipp Maier36291392018-07-25 09:40:44 +02001938
1939 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001940
1941 setverdict(pass);
1942 }
1943
Philipp Maier1ac13982021-05-07 23:06:07 +02001944 /* Create one connection in loopback mode, test if the RTP packets are
1945 * actually reflected */
1946 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001947 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001948 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001949 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1950 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1951 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001952
1953 /* Same as above, but we will intenionally not tell the MGW where to
1954 * send the outgoing traffic. The connection is still created in
1955 * loopback mode, so the MGW should take the originating address from
1956 * the incoming RTP packet and send it back to the source */
1957 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001958 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001959 }
1960
1961
Philipp Maier7df85f62018-07-25 10:26:09 +02001962 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1963 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001964 var RtpFlowData flow[2];
1965 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001966 var MgcpResponse resp;
1967 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1968 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001969 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001970
1971 f_init(ep);
1972
1973 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001974 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001975 /* bind local RTP emulation sockets */
1976 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001977 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001978
1979 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001980 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001981 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001982 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1983
1984 if (bidir) {
1985 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1986 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1987
1988 /* Note: When we test bidirectional we may
1989 * loose packets during switch off because
1990 * both ends are transmitting and we only
1991 * can switch them off one by one. */
1992 tolerance := 3;
1993 } else {
1994 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1995 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1996 }
1997
1998 f_sleep(1.0);
1999
2000 f_flow_delete(RTPEM[1]);
2001 f_flow_delete(RTPEM[0], ep, call_id);
2002
2003 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2004 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2005 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
2006 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002007 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002008 }
2009
Philipp Maier36291392018-07-25 09:40:44 +02002010 f_rtpem_stats_err_check(stats[0]);
2011 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02002012
Philipp Maier2321ef92018-06-27 17:52:04 +02002013 setverdict(pass);
2014 }
2015
2016 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
2017 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02002018 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02002019 }
2020
2021 /* create two local RTP emulations; create two connections on MGW EP,
2022 * exchange some data in both directions */
2023 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02002024 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
2025 }
2026
2027 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
2028 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
2029 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
2030 }
2031
2032 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
2033 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
2034 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02002035 }
2036
2037 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002038 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002039 charstring local_ip_b, charstring remote_ip_b,
2040 uint7_t payload_type := 3,
2041 charstring codec_name := "GSM/8000/1") runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02002042 var RtpFlowData flow[2];
2043 var RtpemStats stats[2];
2044 var MgcpResponse resp;
2045 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2046 var MgcpCallId call_id := '1227'H;
2047 var integer num_pkts_tx[2];
2048 var integer temp;
2049
2050 f_init(ep);
2051
2052 /* Create the first connection in receive only mode */
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002053 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, payload_type, codec_name));
Philipp Maier2321ef92018-06-27 17:52:04 +02002054 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002055 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02002056
2057 /* Create the second connection. This connection will be also
2058 * in receive only mode */
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002059 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, payload_type, codec_name));
Philipp Maier2321ef92018-06-27 17:52:04 +02002060 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002061 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02002062
2063 /* The first leg starts transmitting */
2064 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2065 f_sleep(0.5);
2066 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2067 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002068 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002069 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002070 }
2071 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2072 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002073 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002074 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002075 }
2076
2077 /* The second leg starts transmitting a little later */
2078 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2079 f_sleep(1.0);
2080 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2081 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002082 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002083 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002084 }
2085 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2086 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002087 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002088 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002089 }
2090
2091 /* The first leg will now be switched into bidirectional
2092 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002093 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2094 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2095 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002096 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2097 f_sleep(0.5);
2098 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002099 if (stats[0].num_pkts_rx_err_disabled != 0) {
2100 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002101 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002102 }
2103 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2104 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002105 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002106 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002107 }
2108
2109 /* When the second leg is switched into bidirectional mode
2110 * as well, then the MGW will connect the two together and
2111 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02002112 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2113 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002114 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002115 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2116 f_sleep(2.0);
2117
2118 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2119 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2120
2121 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2122 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002123 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002124 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002125 }
2126
2127 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2128 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002129 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002130 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002131 }
Philipp Maier36291392018-07-25 09:40:44 +02002132
2133 f_rtpem_stats_err_check(stats[0]);
2134 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02002135
2136 /* Tear down */
2137 f_flow_delete(RTPEM[0]);
2138 f_flow_delete(RTPEM[1], ep, call_id);
2139 setverdict(pass);
2140 }
2141
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002142 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
2143 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2144 mp_local_ipv4, mp_remote_ipv4);
2145 }
2146
2147 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
2148 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
2149 mp_local_ipv6, mp_remote_ipv6);
2150 }
2151
2152 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
2153 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2154 mp_local_ipv6, mp_remote_ipv6);
2155 }
2156
Oliver Smith871f45a2023-01-24 16:21:50 +01002157 testcase TC_two_crcx_mdcx_and_rtp_clearmode() runs on dummy_CT {
2158 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2159 mp_local_ipv4, mp_remote_ipv4,
2160 120, /* 3GPP TS 48.103 table 5.4.2.2.1 */
2161 "CLEARMODE/8000");
2162 }
2163
Philipp Maier2321ef92018-06-27 17:52:04 +02002164 /* Test what happens when two RTP streams from different sources target
2165 * a single connection. Is the unsolicited stream properly ignored? */
2166 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2167 var RtpFlowData flow[2];
2168 var RtpemStats stats[2];
2169 var MgcpResponse resp;
2170 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2171 var MgcpCallId call_id := '1234321326'H;
2172 var integer unsolicited_port := 10002;
2173
2174 f_init(ep);
2175
2176 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002177 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002178 /* bind local RTP emulation sockets */
2179 flow[0].em.portnr := 10000;
2180 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2181
2182 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002183 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002184 flow[1].em.portnr := 20000;
2185 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002186
2187 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2188 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2189
Philipp Maier2321ef92018-06-27 17:52:04 +02002190 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002191
Philipp Maier2321ef92018-06-27 17:52:04 +02002192 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002193 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2194 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002195 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2196
2197 f_sleep(0.5);
2198
Daniel Willmanna069d382018-12-13 13:53:33 +01002199 /* Stop transmitting packets and tear down the flows */
2200 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002201 f_flow_delete(RTPEM[0]);
2202 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002203
2204 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2205 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2206 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2207 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002208 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002209 }
2210
Philipp Maier36291392018-07-25 09:40:44 +02002211 f_rtpem_stats_err_check(stats[0]);
2212 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002213
Harald Weltebb7523b2018-03-29 08:52:01 +02002214 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002215 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002216
Philipp Maier2321ef92018-06-27 17:52:04 +02002217 /* Test a handover situation. We first create two connections transmit
2218 * some data bidirectionally. Then we will simulate a handover situation. */
2219 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2220 var RtpFlowData flow[2];
2221 var RtpemStats stats[3];
2222 var MgcpResponse resp;
2223 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2224 var MgcpCallId call_id := '76338'H;
2225 var integer port_old;
2226
2227 f_init(ep);
2228
2229 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002230 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002231 /* bind local RTP emulation sockets */
2232 flow[0].em.portnr := 10000;
2233 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2234
2235 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002236 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002237 flow[1].em.portnr := 20000;
2238 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2239
2240 /* Normal rtp flow for one second */
2241 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2242 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2243 f_sleep(1.0);
2244
2245 /* Now switch the flow over to a new port (BTS) */
2246 port_old := flow[0].em.portnr;
2247 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002248 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002249 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002250 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002251
2252 /* When handing over a call, the old source may still keep
2253 * transmitting for a while. We simulate this by injecting
2254 * some unsolicited packets on the behalf of the old source,
2255 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002256 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2257 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002258 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2259 f_sleep(1.0);
2260 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2261 f_sleep(1.0);
2262
2263 /* Terminate call */
2264 f_flow_delete(RTPEM[0]);
2265 f_flow_delete(RTPEM[1], ep, call_id);
2266
2267 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2268 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2269 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2270 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002271 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002272 }
2273 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2274 if (stats[2].num_pkts_rx_err_disabled != 0) {
2275 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002276 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002277 }
2278
Philipp Maier36291392018-07-25 09:40:44 +02002279 f_rtpem_stats_err_check(stats[0]);
2280 f_rtpem_stats_err_check(stats[1]);
2281 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002282
Philipp Maier2321ef92018-06-27 17:52:04 +02002283 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002284 }
Harald Weltef53f1642017-11-18 19:57:11 +01002285
Andreas Eversbergdb99cb02023-07-12 11:44:11 +02002286 testcase TC_two_crcx_confecho_sendonly_rtp() runs on dummy_CT {
2287 var RtpFlowData flow[2];
2288 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2289 var MgcpCallId call_id := '1225'H;
2290 var RtpemStats stats[2];
2291
2292 f_init(ep);
2293
2294 /* "Talker" is sending to MGW and receives echo. */
2295 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "GSM-HR-08/8000/1"));
2296 flow[0].em.portnr := 10000;
2297 f_flow_create(RTPEM[0], ep, call_id, "confecho", flow[0]);
2298
2299 /* "Listener" receives from MGW. */
2300 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "GSM-HR-08/8000/1"));
2301 flow[1].em.portnr := 20000;
2302 f_flow_create(RTPEM[1], ep, call_id, "sendonly", flow[1]);
2303
2304
2305 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2306 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2307 f_sleep(1.0);
2308 f_flow_delete(RTPEM[0]);
2309 f_flow_delete(RTPEM[1], ep, call_id);
2310
2311 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2312 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2313
2314 /* The "Talker" will receive his RTP, so TX must match RX.
2315 * As RTP from "Listener" is ignored, no extra packets shall be received. */
2316 if (stats[0].num_pkts_tx != stats[0].num_pkts_rx) {
2317 setverdict(fail, "Talker does not receive as many packets as it transmits!");
2318 }
2319 if (stats[0].bytes_payload_tx != stats[0].bytes_payload_rx) {
2320 setverdict(fail, "Talker does not receive as many payload as it transmits!");
2321 }
2322
2323 /* The "Listener" will also receive RTP of the "Talker",
2324 * so TX of "Talker" must match RX of "Listener". */
2325 if (stats[0].num_pkts_tx != stats[1].num_pkts_rx) {
2326 setverdict(fail, "Listener does not receive as many packets as talker transmits!");
2327 }
2328 if (stats[0].bytes_payload_tx != stats[1].bytes_payload_rx) {
2329 setverdict(fail, "Listener does not receive as many payload as talker transmits!");
2330 }
2331
2332 f_rtpem_stats_err_check(stats[0]);
2333 f_rtpem_stats_err_check(stats[1]);
2334
2335 setverdict(pass);
2336 }
2337
Philipp Maier6d4e0942019-02-21 17:35:01 +01002338
2339 /* create two local RTP emulations; create two connections on MGW EP, see if
Philipp Maier8ed48c52023-02-07 11:24:31 +01002340 * exchanged data is converted between ts101318 and rfc5993 */
2341 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 +01002342 var RtpFlowData flow[2];
2343 var RtpemStats stats[2];
2344 var MgcpResponse resp;
2345 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2346 var MgcpCallId call_id := '1226'H;
2347
2348 f_init(ep);
2349
2350 /* Turn on conversion mode */
2351 f_vty_enter_config(MGWVTY);
2352 f_vty_transceive(MGWVTY, "mgcp");
2353 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2354
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002355 /* Connection #0 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002356 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000", fmtp0));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002357 /* bind local RTP emulation sockets */
2358 flow[0].em.portnr := 10000;
2359 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002360 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2361 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2362 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0;
2363 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002364 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2365
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002366 /* Connection #1 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002367 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000", fmtp1));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002368 flow[1].em.portnr := 20000;
2369 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002370 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2371 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2372 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := pl1;
2373 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := pl1;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002374 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2375
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002376 /* Send RTP packets to connection #0, receive on connection #1 */
2377 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2378 f_sleep(0.5);
2379 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002380 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002381 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2382 f_sleep(0.5);
2383 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002384
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002385 /* Send RTP packets to connection #1, receive on connection #0 */
2386 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2387 f_sleep(0.5);
2388 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2389 f_sleep(1.0);
2390 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2391 f_sleep(0.5);
2392 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2393
2394 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002395 f_flow_delete(RTPEM[0]);
2396 f_flow_delete(RTPEM[1], ep, call_id);
2397
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002398 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002399 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2400 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002401 f_rtpem_stats_err_check(stats[0]);
2402 f_rtpem_stats_err_check(stats[1]);
2403
2404 /* Turn off conversion mode */
2405 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2406
2407 setverdict(pass);
2408 }
2409
Philipp Maier8ed48c52023-02-07 11:24:31 +01002410 const octetstring rtp_hr_gsm_ts101318 := '0b11b3eede60be4e3ec68838c7b5'O;
2411 const octetstring rtp_hr_gsm_rfc5993 := '000b11b3eede60be4e3ec68838c7b5'O;
2412
2413 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2414 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "", "");
2415 }
2416
2417 testcase TC_ts101318_rfc5993_rtp_conversion_fmtp() runs on dummy_CT {
2418 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "gsm-hr-format=ts101318", "gsm-hr-format=rfc5993");
2419 }
2420
Philipp Maier4f764ce2019-03-07 10:54:10 +01002421 /* create two local RTP emulations; create two connections on MGW EP, see if
Philipp Maiereb5e8132023-04-13 16:20:37 +02002422 * exchanged data is converted between AMR octet-aligned and bandwidth
Philipp Maier4f764ce2019-03-07 10:54:10 +01002423 * efficient-mode */
Philipp Maier71484282023-04-04 18:00:53 +02002424 function f_TC_amr_x_x_rtp_conversion(charstring fmtp0, octetstring pl0,
2425 charstring fmtp1a, octetstring pl1a,
2426 charstring fmtp1b, octetstring pl1b) runs on dummy_CT {
Philipp Maier4f764ce2019-03-07 10:54:10 +01002427 var RtpFlowData flow[2];
2428 var RtpemStats stats[2];
2429 var MgcpResponse resp;
2430 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2431 var MgcpCallId call_id := '1226'H;
2432
2433 f_init(ep);
2434
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002435 /* Connection #0 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002436 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 96, "AMR/8000", fmtp0));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002437 /* bind local RTP emulation sockets */
2438 flow[0].em.portnr := 10000;
2439 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002440 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2441 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2442 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0;
2443 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0;
Philipp Maier4f764ce2019-03-07 10:54:10 +01002444 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2445
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002446 /* Connection #1 (Bidirectional) */
Philipp Maier71484282023-04-04 18:00:53 +02002447 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000", fmtp1a));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002448 flow[1].em.portnr := 20000;
2449 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maier71484282023-04-04 18:00:53 +02002450 flow[1].rtp_cfg.rx_payloads := {};
2451 flow[1].rtp_cfg.tx_payloads := {};
2452 if (pl1a != ''O) {
2453 flow[1].rtp_cfg.rx_payloads := flow[1].rtp_cfg.rx_payloads & {{112, pl1a}};
2454 flow[1].rtp_cfg.tx_payloads := flow[1].rtp_cfg.tx_payloads & {{112, pl1a}};
2455 }
2456
2457 /* The second fmtp parameter is to simulate a call agent that offers the transmission both modes. */
2458 if (fmtp1b != "") {
2459 flow[1].codec_descr := flow[1].codec_descr & {{113, "AMR/8000", fmtp1b}};
2460 if (pl1b != ''O) {
2461 flow[1].rtp_cfg.rx_payloads := flow[1].rtp_cfg.rx_payloads & {{113, pl1b}};
2462 flow[1].rtp_cfg.tx_payloads := flow[1].rtp_cfg.tx_payloads & {{113, pl1b}};
2463 }
2464 }
2465
Philipp Maier4f764ce2019-03-07 10:54:10 +01002466 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2467
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002468 /* Send RTP packets to connection #0, receive on connection #1 */
2469 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2470 f_sleep(0.5);
2471 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002472 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002473 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2474 f_sleep(0.5);
2475 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002476
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002477 /* Send RTP packets to connection #1, receive on connection #0 */
2478 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2479 f_sleep(0.5);
2480 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2481 f_sleep(1.0);
2482 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2483 f_sleep(0.5);
2484 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2485
2486 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002487 f_flow_delete(RTPEM[0]);
2488 f_flow_delete(RTPEM[1], ep, call_id);
2489
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002490 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002491 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2492 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002493 f_rtpem_stats_err_check(stats[0]);
2494 f_rtpem_stats_err_check(stats[1]);
2495
2496 setverdict(pass);
2497 }
2498
Philipp Maier882843d2020-05-25 15:33:13 +02002499 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2500 * functions are real world AMR RTP payloads including AMR header. The
2501 * payloads were extracted from a trace with known good payloads. */
2502
Philipp Maiercd756ed2023-02-13 11:12:24 +01002503 const octetstring rtp_amr_5_90k_oa := '2014e959f35fdfe5e9667ffbc088818088'O;
2504 const octetstring rtp_amr_5_90k_bwe := '217a567cd7f7f97a599ffef022206022'O;
2505 const octetstring rtp_amr_5_15k_oa := '100c4e9ba850e30d5d53d04de41e7c'O;
2506 const octetstring rtp_amr_5_15k_bwe := '10d3a6ea1438c35754f41379079f'O;
2507
Philipp Maier71484282023-04-04 18:00:53 +02002508 /* Only one codec on each side */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002509 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier71484282023-04-04 18:00:53 +02002510 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 +01002511 }
Philipp Maier4f764ce2019-03-07 10:54:10 +01002512 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
Philipp Maier71484282023-04-04 18:00:53 +02002513 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa, "octet-align=1", rtp_amr_5_15k_oa, "", ''O);
2514 }
2515 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2516 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 +01002517 }
2518
Philipp Maier71484282023-04-04 18:00:53 +02002519 /* Only one codec on one side, two codecs (compatibility) on other side. The payloads are the same on both
2520 * sides, so the expectation is that conversion must not be performed. Each test is done with both formats in
2521 * two different configurations.*/
2522 testcase TC_amr_oa_oa_no_bwe_rtp_conversion() runs on dummy_CT {
2523 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa,
2524 "octet-align=1", rtp_amr_5_15k_oa,
2525 "octet-align=0", ''O); /* We expect to see NO bandwidth efficient packets! */
2526 }
2527 testcase TC_amr_oa_no_bwe_oa_rtp_conversion() runs on dummy_CT {
2528 /* (Same as above but flipped on the opposite side) */
2529 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa,
2530 "octet-align=0", ''O, /* We expect to see NO bandwidth efficient packets! */
2531 "octet-align=1", rtp_amr_5_15k_oa);
2532 }
2533 testcase TC_amr_bwe_bwe_no_oa_rtp_conversion() runs on dummy_CT {
2534 f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe,
2535 "octet-align=0", rtp_amr_5_15k_bwe,
2536 "octet-align=1", ''O); /* We expect to see NO octet aligned packets! */
2537 }
2538 testcase TC_amr_bwe_no_oa_bwe_rtp_conversion() runs on dummy_CT {
2539 /* (Same as above but flipped on the opposite side) */
2540 f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe,
2541 "octet-align=1", ''O, /* We expect to see NO octet aligned packets! */
2542 "octet-align=0", rtp_amr_5_15k_bwe);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002543 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002544
Harald Weltee636afd2017-09-17 16:24:09 +08002545 /* TODO: Double-DLCX (no retransmission) */
2546
2547
2548
2549 /* TODO: AUEP (various) */
2550 /* TODO: RSIP (various) */
2551 /* TODO: RQNT (various) */
2552 /* TODO: EPCF (various) */
2553 /* TODO: AUCX (various) */
2554 /* TODO: invalid verb (various) */
2555
Oliver Smith021141e2019-06-25 12:09:01 +02002556
2557 testcase TC_conn_timeout() runs on dummy_CT {
2558 var RtpFlowData flow;
2559 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2560 var MgcpCallId call_id := '1225'H;
2561 var MGCP_RecvFrom mrf;
2562
2563 f_init(ep);
2564 log("Setting conn-timeout to 1s");
Pau Espin Pedrol30da2392023-09-27 16:49:20 +02002565 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1");
Oliver Smith021141e2019-06-25 12:09:01 +02002566
2567 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002568 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002569 flow.em.portnr := 10000;
2570 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2571 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2572 f_sleep(1.5);
2573
2574 log("Stopping for 0.5s and resuming");
2575 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2576 f_sleep(0.5);
2577 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2578 f_sleep(0.1);
2579
2580 log("Stopping for 1.5s, expecting to run into timeout");
2581 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2582 f_sleep(1.5);
2583
2584 log("Resuming should fail now");
2585 f_rtpem_conn_refuse_expect(RTPEM[0]);
2586 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2587 f_sleep(0.2);
2588 f_rtpem_conn_refuse_verify(RTPEM[0]);
2589
Pau Espin Pedrol30da2392023-09-27 16:49:20 +02002590 log("Setting conn-timeout back to 0 (disabled)");
2591 f_vty_config(MGWVTY, "mgcp", "conn-timeout 0");
2592
Oliver Smith021141e2019-06-25 12:09:01 +02002593 setverdict(pass);
2594 }
2595
Philipp Maier2609c752020-07-08 12:38:09 +02002596 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2597 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2598 var template MgcpCommand cmd;
2599 var MgcpResponse resp;
2600 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2601 var MgcpCallId call_id := '8376F297'H;
2602
2603 f_init(ep);
2604
2605 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2606 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2607
2608 f_dlcx_ok(ep);
2609
2610 setverdict(pass);
2611 }
2612
2613 /* Test what happens when overlapping endpoints are selected (E1) */
2614 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2615 var template MgcpCommand cmd;
2616 var MgcpResponse resp;
2617 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2618 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2619 var MgcpCallId call_id_1 := '8376F297'H;
2620 var MgcpCallId call_id_2 := '837AF2A7'H;
2621
2622 f_init();
2623
2624 /* ep_1 and ep_2 are overlapping, selecting both one after
2625 * another should work fine: */
2626 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2627 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2628 f_dlcx_ok(ep_1);
2629 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2630 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2631 f_dlcx_ok(ep_2);
2632
2633 /* When ep_1 is serving a call we can not select ep_2 becaus
2634 * it is overlapping with ep_1 */
2635 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2636 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2637 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2638 resp := mgcp_transceive_mgw(cmd, ?);
2639 if (resp.line.code != "501") {
2640 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2641 }
2642 f_dlcx_ok(ep_1);
2643
2644 setverdict(pass);
2645 }
2646
2647 /* Create one connection in loopback mode, test if the RTP packets are
2648 * actually reflected */
2649 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2650 var RtpFlowData flow;
2651 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2652 var MgcpCallId call_id := '12250989'H;
2653 var RtpemStats stats;
2654
2655 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002656 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002657 flow.em.portnr := 10000;
2658 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2659
2660 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2661 f_sleep(1.0);
2662 f_flow_delete(RTPEM[0], ep, call_id);
2663
2664 stats := f_rtpem_stats_get(RTPEM[0]);
2665
2666 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2667 setverdict(fail);
2668 }
2669 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2670 setverdict(fail);
2671 }
2672
2673 f_rtpem_stats_err_check(stats);
2674
2675 setverdict(pass);
2676 }
2677
Philipp Maier13aff992022-06-30 16:19:59 +02002678 /* test valid wildcarded DLCX on an E1 trunk */
2679 testcase TC_e1_dlcx_wildcarded() runs on dummy_CT {
2680 var template MgcpCommand cmd;
2681 var MgcpEndpoint ep;
2682 var MgcpCallId call_id := '8376F297'H;
2683 var integer n_e1_ts := 4;
2684 var StatsDExpects expect;
2685
2686 f_init();
2687
2688 /* Open a few E1 timeslots */
2689 for (var integer i := 0; i < n_e1_ts; i := i+1) {
2690 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-0@" & c_mgw_domain;
2691 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2692 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2693 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-2@" & c_mgw_domain;
2694 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2695 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2696 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-4@" & c_mgw_domain;
2697 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2698 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2699 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-6@" & c_mgw_domain;
2700 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2701 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2702 }
2703
2704 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
2705 * occupied endpoints */
2706 f_sleep(1.0)
2707 expect := {
2708 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := n_e1_ts * 4, max := n_e1_ts * 4}
2709 };
2710 f_statsd_expect(expect);
2711
2712 /* Send wildcarded DLCX */
2713 var template MgcpResponse rtmpl := {
2714 line := {
2715 code := "200",
2716 string := ?
2717 },
2718 params:= { },
2719 sdp := omit
2720 };
2721 ep := "ds/e1-1/*@" & c_mgw_domain;
2722 cmd := ts_DLCX(get_next_trans_id(), ep);
2723 mgcp_transceive_mgw(cmd, rtmpl);
2724
2725 /* Query a the statsd once to ensure that intermediate results are pulled from the
2726 * pipeline. The second query (below) will return the actual result. */
2727 expect := {
2728 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := n_e1_ts * 4}
2729 };
2730 f_statsd_expect(expect);
2731
2732 /* The second query must resturn a result with 0 endpoints in use. */
2733 expect := {
2734 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := 0}
2735 };
2736 f_statsd_expect(expect);
2737
2738 setverdict(pass);
2739 }
2740
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002741 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2742 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2743 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2744 var template MgcpCommand cmd;
2745 var MgcpResponse resp;
2746 var MgcpCallId call_id := '1234'H;
2747 var MgcpConnectionId conn_id;
2748
2749 f_init(ep);
2750
2751 /* create the connection on the MGW */
2752 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2753 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2754 conn_id := extract_conn_id(resp);
2755
2756 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2757 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2758 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2759 valueof(ts_SDP_ptime(20)) });
2760 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2761
2762 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2763 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2764 }
2765 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2766 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2767 }
2768
2769 /* clean-up */
2770 f_dlcx_ok(ep, call_id);
2771 setverdict(pass);
2772 }
2773
2774 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2775 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2776 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2777 var template MgcpCommand cmd;
2778 var MgcpResponse resp;
2779 var MgcpCallId call_id := '1234'H;
2780 var MgcpConnectionId conn_id;
2781
2782 f_init(ep);
2783
2784 /* create the connection on the MGW */
2785 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2786 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2787 conn_id := extract_conn_id(resp);
2788
2789 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2790 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2791 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2792 valueof(ts_SDP_ptime(20)) });
2793 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2794
2795 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2796 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2797 }
2798 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2799 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2800 }
2801
2802 /* clean-up */
2803 f_dlcx_ok(ep, call_id);
2804 setverdict(pass);
2805 }
2806
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002807 /* create two local emulations and pass data in both directions */
2808 function f_two_crcx_mdcx_data_transfer(MgcpEndpoint ep, MgcpCallId call_id, inout RtpFlowData flow_a,
2809 inout RtpFlowData flow_b, boolean tear_down_rtp := true) runs on dummy_CT {
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002810 var RtpemStats stats[2];
2811 var MgcpResponse resp;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002812 var integer num_pkts_tx[2];
2813 var integer temp;
2814
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002815 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002816 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow_a, true);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002817 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2818
2819 /* Create the second connection. This connection will be also
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002820 * in receive only mode (CN side, regular RTP) */
2821 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow_b, true);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002822 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2823
2824 /* The first leg starts transmitting */
2825 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2826 f_sleep(0.5);
2827 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2828 if (stats[0].num_pkts_rx_err_disabled != 0) {
2829 setverdict(fail, "received packets from MGW on recvonly connection 0");
2830 mtc.stop;
2831 }
2832 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2833 if (stats[1].num_pkts_rx_err_disabled != 0) {
2834 setverdict(fail, "received packets from MGW on recvonly connection 1");
2835 mtc.stop;
2836 }
2837
2838 /* The second leg starts transmitting a little later */
2839 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2840 f_sleep(1.0);
2841 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2842 if (stats[0].num_pkts_rx_err_disabled != 0) {
2843 setverdict(fail, "received packets from MGW on recvonly connection 0");
2844 mtc.stop;
2845 }
2846 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2847 if (stats[1].num_pkts_rx_err_disabled != 0) {
2848 setverdict(fail, "received packets from MGW on recvonly connection 1");
2849 mtc.stop;
2850 }
2851
2852 /* The first leg will now be switched into bidirectional
2853 * mode, but we do not expect any data coming back yet. */
2854 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2855 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2856 num_pkts_tx[1] := stats[1].num_pkts_tx;
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002857 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow_a);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002858 f_sleep(0.5);
2859 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2860 if (stats[0].num_pkts_rx_err_disabled != 0) {
2861 setverdict(fail, "received packets from MGW on recvonly connection 0");
2862 mtc.stop;
2863 }
2864 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2865 if (stats[1].num_pkts_rx_err_disabled != 0) {
2866 setverdict(fail, "received packets from MGW on recvonly connection 1");
2867 mtc.stop;
2868 }
2869
2870 /* When the second leg is switched into bidirectional mode
2871 * as well, then the MGW will connect the two together and
2872 * we should see RTP streams passing through from both ends. */
2873 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2874 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2875 num_pkts_tx[0] := stats[0].num_pkts_tx;
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002876 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow_b);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002877 f_sleep(2.0);
2878
2879 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2880 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2881
2882 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2883 if (temp > 3 or temp < -3) {
2884 setverdict(fail, "number of packets not within normal parameters:", temp);
2885 mtc.stop;
2886 }
2887
2888 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2889 if (temp > 3 or temp < -3) {
2890 setverdict(fail, "number of packets not within normal parameters:", temp);
2891 mtc.stop;
2892 }
2893
2894 f_rtpem_stats_err_check(stats[0]);
2895 f_rtpem_stats_err_check(stats[1]);
2896
2897 /* Tear down */
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002898 if (tear_down_rtp) {
2899 f_flow_delete(RTPEM[0]);
2900 f_flow_delete(RTPEM[1], ep, call_id);
2901 }
2902 setverdict(pass);
2903 }
2904
2905 /* create two local RTP+IuUP emulations and pass data in both directions */
2906 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
2907 IuUP_RabFlowCombinationList rfcl_a,
2908 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2909 var RtpFlowData flow[2];
2910 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2911 var MgcpCallId call_id := '1227'H;
2912
2913 f_init(ep);
2914
2915 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2916 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2917 flow[0].em.portnr := 10000;
2918 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2919 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2920 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2921 flow[0].rtp_cfg.iuup_mode := true;
2922 flow[0].rtp_cfg.iuup_cfg.active_init := true;
2923 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
2924
2925 /* Create the second connection. This connection will be also
2926 * in receive only mode (CN side, IuUP-Init passive) */
2927 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2928 flow[1].em.portnr := 20000;
2929 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2930 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2931 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2932 flow[1].rtp_cfg.iuup_mode := true;
2933 flow[1].rtp_cfg.iuup_cfg.active_init := false;
2934
2935 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], true);
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002936 setverdict(pass);
2937 }
2938 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002939 var template (value) IuUP_RabFlowCombinationList rfcl := {
2940 t_IuUP_RFC_AMR_12_2(0),
2941 t_IuUP_RFC_AMR_SID(1),
2942 t_IuUP_RFC_AMR_NO_DATA(2)
2943 };
2944 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2945 mp_local_ipv4, mp_remote_ipv4);
2946 }
2947 /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */
2948 testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT {
2949 var template (value) IuUP_RabFlowCombinationList rfcl := {
2950 t_IuUP_RFC_AMR_12_2(1),
2951 t_IuUP_RFC_AMR_SID(2),
2952 t_IuUP_RFC_AMR_NO_DATA(0)
2953 };
2954 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002955 mp_local_ipv4, mp_remote_ipv4);
2956 }
2957
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002958 /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */
2959 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 +02002960 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002961 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2962 var RtpFlowData flow[2];
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002963 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2964 var MgcpCallId call_id := '1227'H;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002965
2966 f_init(ep);
2967
2968 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2969 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2970 flow[0].em.portnr := 10000;
2971 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002972 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2973 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2974 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
2975 /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
2976 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002977 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002978 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002979 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002980
2981 /* Create the second connection. This connection will be also
2982 * in receive only mode (CN side, regular RTP) */
2983 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
2984 flow[1].em.portnr := 20000;
2985 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002986 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2987 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2988 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
2989 /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
2990 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002991 flow[1].rtp_cfg.iuup_mode := false;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002992
Pau Espin Pedrol74d75b12023-09-27 14:45:51 +02002993 f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], true);
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002994 setverdict(pass);
2995 }
2996 testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002997 var template (value) IuUP_RabFlowCombinationList rfcl := {
2998 t_IuUP_RFC_AMR_12_2(0),
2999 t_IuUP_RFC_AMR_SID(1),
3000 t_IuUP_RFC_AMR_NO_DATA(2)
3001 };
3002 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
3003 mp_local_ipv4, mp_remote_ipv4);
3004 }
3005 /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */
3006 testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT {
3007 var template (value) IuUP_RabFlowCombinationList rfcl := {
3008 t_IuUP_RFC_AMR_12_2(1),
3009 t_IuUP_RFC_AMR_SID(2),
3010 t_IuUP_RFC_AMR_NO_DATA(0)
3011 };
3012 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003013 mp_local_ipv4, mp_remote_ipv4);
3014 }
3015
Harald Welte00a067f2017-09-13 23:27:17 +02003016 control {
3017 execute(TC_selftest());
Pau Espin Pedrolb26d4462023-06-15 11:38:02 +02003018 execute(TC_auep_null());
Harald Welte3c6ebb92017-09-16 00:56:57 +08003019 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02003020 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01003021 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08003022 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02003023 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08003024 execute(TC_crcx_early_bidir_mode());
3025 execute(TC_crcx_unsupp_param());
3026 execute(TC_crcx_missing_callid());
3027 execute(TC_crcx_missing_mode());
3028 execute(TC_crcx_unsupp_packet_intv());
3029 execute(TC_crcx_illegal_double_lco());
3030 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01003031 execute(TC_crcx_wildcarded());
3032 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08003033 execute(TC_mdcx_without_crcx());
3034 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02003035 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01003036 execute(TC_mdcx_wildcarded());
3037 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01003038 execute(TC_crcx_and_dlcx_ep_callid_connid());
3039 execute(TC_crcx_and_dlcx_ep_callid());
3040 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01003041 execute(TC_crcx_and_dlcx_ep_callid_inval());
3042 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01003043 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01003044
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003045 execute(TC_crcx_osmux_wildcard());
3046 execute(TC_crcx_osmux_fixed());
3047 execute(TC_crcx_osmux_fixed_twice());
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02003048 execute(TC_crcx_osmux_257());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003049 execute(TC_one_crcx_receive_only_osmux());
3050 execute(TC_one_crcx_loopback_osmux());
3051 execute(TC_two_crcx_and_rtp_osmux());
3052 execute(TC_two_crcx_and_rtp_osmux_bidir());
Pau Espin Pedrol26258472022-10-25 12:51:24 +02003053 execute(TC_two_crcx_and_rtp_osmux_bidir_amr_bwe());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003054 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
3055 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
3056
Harald Welte33d82162017-12-28 03:21:57 +01003057 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02003058
3059 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02003060
3061 execute(TC_one_crcx_receive_only_rtp());
3062 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02003063 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02003064 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02003065 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02003066 execute(TC_two_crcx_diff_pt_and_rtp());
3067 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02003068 execute(TC_two_crcx_mdcx_and_rtp());
3069 execute(TC_two_crcx_and_unsolicited_rtp());
3070 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Andreas Eversbergdb99cb02023-07-12 11:44:11 +02003071 execute(TC_two_crcx_confecho_sendonly_rtp());
Philipp Maier6d4e0942019-02-21 17:35:01 +01003072 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier8ed48c52023-02-07 11:24:31 +01003073 execute(TC_ts101318_rfc5993_rtp_conversion_fmtp());
Philipp Maier4f764ce2019-03-07 10:54:10 +01003074 execute(TC_amr_oa_bwe_rtp_conversion());
3075 execute(TC_amr_oa_oa_rtp_conversion());
3076 execute(TC_amr_bwe_bwe_rtp_conversion());
Philipp Maier71484282023-04-04 18:00:53 +02003077 execute(TC_amr_oa_oa_no_bwe_rtp_conversion());
3078 execute(TC_amr_oa_no_bwe_oa_rtp_conversion());
3079 execute(TC_amr_bwe_bwe_no_oa_rtp_conversion());
3080 execute(TC_amr_bwe_no_oa_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02003081
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01003082 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02003083
3084 execute(TC_e1_crcx_and_dlcx_ep());
3085 execute(TC_e1_crcx_with_overlap());
3086 execute(TC_e1_crcx_loopback());
Philipp Maier13aff992022-06-30 16:19:59 +02003087 execute(TC_e1_dlcx_wildcarded());
Philipp Maier2609c752020-07-08 12:38:09 +02003088
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02003089 execute(TC_crcx_mdcx_ip4());
3090 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02003091 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
3092 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
3093 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
3094 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
3095 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
3096 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
3097 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
3098 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02003099
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003100 execute(TC_two_crcx_mdcx_and_iuup());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003101 execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered());
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003102 execute(TC_two_crcx_mdcx_and_iuup_rtp());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003103 execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered());
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003104
Oliver Smith871f45a2023-01-24 16:21:50 +01003105 execute(TC_two_crcx_mdcx_and_rtp_clearmode());
3106
Philipp Maier37965082021-05-25 16:44:25 +02003107 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
3108 * older versions of osmo-mgw. This eventually leads into
3109 * a failure of all subsequent testcases, so it is important
3110 * not to add new testcaes after this one. */
3111 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02003112 }
3113}