blob: 28b71039fc6052dc13a735f08bd892925a40142c [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
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100689 function f_crcx(charstring ep_prefix) runs on dummy_CT {
690 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800691 var template MgcpCommand cmd;
692 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100693 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800694
Harald Welteedc45c12017-11-18 19:15:05 +0100695 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800696
Harald Welteba62c8c2017-11-18 18:26:49 +0100697 /* create the connection on the MGW */
698 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100699 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100700 extract_conn_id(resp);
701
702 /* clean-up */
703 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100704 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100705
Philipp Maier45635f42018-06-05 17:28:02 +0200706 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
707 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
708 var template MgcpCommand cmd;
709 var MgcpResponse resp;
710 var MgcpCallId call_id := '1234'H;
711
712 f_init(ep);
713
714 /* create the connection on the MGW */
715 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
716 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
717 extract_conn_id(resp);
718
719 /* clean-up */
720 f_dlcx_ok(ep, call_id);
721
722 /* See also OS#2658: Even when we omit the LCO information, we
723 expect the MGW to pick a sane payload type for us. This
724 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200725 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200726 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200727 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200728 }
729
730 /* See also OS#2658: We also expect the MGW to assign a port
731 number to us. */
732 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
733 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200734 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200735 }
736 }
737
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200738 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
739 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
740 var template MgcpCommand cmd;
741 var MgcpResponse resp;
742 var MgcpCallId call_id := '1234'H;
743 var charstring cid_response;
744
745 if (run_init) {
746 f_init(ep, true);
747 }
748
749 /* create the connection on the MGW */
750 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
751 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
752 extract_conn_id(resp);
753
754 /* extract Osmux CID we got assigned by the MGW */
755 var MgcpMessage resp_msg := {
756 response := resp
757 }
758
759 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
760 setverdict(fail, "No Osmux CID in MGCP response", resp);
761 mtc.stop;
762 }
763
764 /* Make sure response is no wildcard */
765 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
766 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
767 mtc.stop;
768 }
769
770 /* clean-up */
771 f_dlcx_ok(ep, call_id);
772 }
773
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100774 /* test valid CRCX without SDP */
775 testcase TC_crcx() runs on dummy_CT {
776 f_crcx(c_mgw_ep_rtpbridge);
777 setverdict(pass);
778 }
779
Philipp Maier45635f42018-06-05 17:28:02 +0200780 /* test valid CRCX without SDP and LCO */
781 testcase TC_crcx_no_lco() runs on dummy_CT {
782 f_crcx_no_lco(c_mgw_ep_rtpbridge);
783 setverdict(pass);
784 }
785
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100786 /* test valid CRCX without SDP (older method without endpoint prefix) */
787 testcase TC_crcx_noprefix() runs on dummy_CT {
788 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800789 setverdict(pass);
790 }
791
792 /* test CRCX with unsupported mode, expect 517 */
793 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
794 var template MgcpCommand cmd;
795 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100796 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100797 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800798 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
799
Harald Welteedc45c12017-11-18 19:15:05 +0100800 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800801
Harald Welteba62c8c2017-11-18 18:26:49 +0100802 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800803 resp := mgcp_transceive_mgw(cmd, rtmpl);
804 setverdict(pass);
805 }
806
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200807 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
808 testcase TC_crcx_osmo_ign() runs on dummy_CT {
809 var template MgcpCommand cmd;
810 var MgcpResponse resp;
811 var MgcpEndpoint ep := "7@" & c_mgw_domain;
812 var MgcpCallId call_id := '3'H;
813
814 f_init(ep);
815
816 /* CRCX 1 7@mgw MGCP 1.0
817 C: 3
818 L: p:20, a:GSM-EFR, nt:IN
819 M: recvonly
820 X-Osmo-IGN: C
821 */
822
823 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
824 cmd.params := {ts_MgcpParCallId(call_id),
825 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
826 t_MgcpParConnMode("recvonly"),
827 t_MgcpParOsmoIGN("C")};
828 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
829 extract_conn_id(resp);
830
831 /* clean-up */
832 f_dlcx_ok(ep, call_id);
833 setverdict(pass);
834 }
835
Harald Welte21ba5572017-09-19 17:55:05 +0800836 /* test CRCX with early bi-directional mode, expect 527 as
837 * bi-diretional media can only be established once both local and
838 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800839 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
840 var template MgcpCommand cmd;
841 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100842 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100843 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800844 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
845
Harald Welteedc45c12017-11-18 19:15:05 +0100846 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800847
Harald Welteba62c8c2017-11-18 18:26:49 +0100848 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800849 resp := mgcp_transceive_mgw(cmd, rtmpl);
850 setverdict(pass);
851 }
852
853 /* test CRCX with unsupported Parameters */
854 testcase TC_crcx_unsupp_param() runs on dummy_CT {
855 var template MgcpCommand cmd;
856 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100857 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100858 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800859 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
860
Harald Welteedc45c12017-11-18 19:15:05 +0100861 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800862
Harald Welteba62c8c2017-11-18 18:26:49 +0100863 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100864 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
865 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
866
Harald Weltee636afd2017-09-17 16:24:09 +0800867 resp := mgcp_transceive_mgw(cmd, rtmpl);
868 setverdict(pass);
869 }
870
871 /* test CRCX with missing CallId */
872 testcase TC_crcx_missing_callid() 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 Weltef91edf32017-12-28 14:16:21 +0100876 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800877
Harald Welteedc45c12017-11-18 19:15:05 +0100878 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800879
Harald Welteba62c8c2017-11-18 18:26:49 +0100880 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800881 cmd.params := {
882 t_MgcpParConnMode("recvonly"),
883 t_MgcpParLocConnOpt("p:20")
884 }
885 resp := mgcp_transceive_mgw(cmd, rtmpl);
886 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100887
Harald Weltee636afd2017-09-17 16:24:09 +0800888 }
889
890 /* test CRCX with missing Mode */
891 testcase TC_crcx_missing_mode() runs on dummy_CT {
892 var template MgcpCommand cmd;
893 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100894 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100895 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100896 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800897
Harald Welteedc45c12017-11-18 19:15:05 +0100898 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800899
Harald Welteba62c8c2017-11-18 18:26:49 +0100900 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800901 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100902 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800903 t_MgcpParLocConnOpt("p:20")
904 }
905 resp := mgcp_transceive_mgw(cmd, rtmpl);
906 setverdict(pass);
907 }
908
909 /* test CRCX with unsupported packetization interval */
910 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
911 var template MgcpCommand cmd;
912 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100913 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100914 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100915 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800916
Harald Welteedc45c12017-11-18 19:15:05 +0100917 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800918
Harald Welteba62c8c2017-11-18 18:26:49 +0100919 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100920 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800921 resp := mgcp_transceive_mgw(cmd, rtmpl);
922 setverdict(pass);
923 }
924
925 /* test CRCX with illegal double presence of local connection option */
926 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
927 var template MgcpCommand cmd;
928 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100929 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100930 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800931 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
932
Harald Welteedc45c12017-11-18 19:15:05 +0100933 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800934
Harald Welteba62c8c2017-11-18 18:26:49 +0100935 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100936 /* p:20 is permitted only once and not twice! */
937 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800938 resp := mgcp_transceive_mgw(cmd, rtmpl);
939 setverdict(pass);
940 }
941
942 /* test valid CRCX with valid SDP */
943 testcase TC_crcx_sdp() runs on dummy_CT {
944 var template MgcpCommand cmd;
945 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100946 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100947 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800948
Harald Welteedc45c12017-11-18 19:15:05 +0100949 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800950
Harald Welteba62c8c2017-11-18 18:26:49 +0100951 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800952 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
953 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
954 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100955 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100956
957 /* clean-up */
958 f_dlcx_ok(ep, call_id);
959
Harald Weltee636afd2017-09-17 16:24:09 +0800960 setverdict(pass);
961 }
962
Philipp Maier5e06cee2018-02-01 18:28:08 +0100963 /* test valid wildcarded CRCX */
964 testcase TC_crcx_wildcarded() runs on dummy_CT {
965 var template MgcpCommand cmd;
966 var MgcpResponse resp;
967 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
968 var MgcpCallId call_id := '1234'H;
969 var MgcpEndpoint ep_assigned;
970 f_init();
971
972 /* create the connection on the MGW */
973 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
974 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
975 extract_conn_id(resp);
976
977 /* extract endpoint name we got assigned by the MGW */
978 var MgcpMessage resp_msg := {
979 response := resp
980 }
981 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
982 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200983 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100984 }
985
986 /* clean-up */
987 f_dlcx_ok(ep_assigned, call_id);
988
989 setverdict(pass);
990 }
991
992 /* test valid wildcarded CRCX */
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200993 type record of MgcpEndpoint MgcpEndpointList;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100994 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maier5e06cee2018-02-01 18:28:08 +0100995 var integer i;
996 var template MgcpCommand cmd;
997 var MgcpResponse resp;
998 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
999 var MgcpCallId call_id := '1234'H;
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001000 var MgcpEndpoint ep_assigned;
1001 var MgcpEndpointList ep_assigned_li := {};
Philipp Maier5e06cee2018-02-01 18:28:08 +01001002 f_init();
1003
1004 /* Exhaust all endpoint resources on the virtual trunk */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001005 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier5e06cee2018-02-01 18:28:08 +01001006 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1007 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1008
1009 /* Make sure we got a connection id */
1010 extract_conn_id(resp);
1011
1012 var MgcpMessage resp_msg := {
1013 response := resp
1014 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001015 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
Philipp Maier5e06cee2018-02-01 18:28:08 +01001016 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001017 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +01001018 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001019 ep_assigned_li := ep_assigned_li & {ep_assigned}
Philipp Maier5e06cee2018-02-01 18:28:08 +01001020 }
1021
1022 /* Try to allocate one more endpoint, which should fail */
1023 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1024 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
1025 resp := mgcp_transceive_mgw(cmd, rtmpl);
1026 setverdict(pass);
1027
1028 /* clean-up */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001029 for (i := 0; i < mp_num_endpoints; i := i+1) {
1030 f_dlcx_ok(ep_assigned_li[i], call_id);
Philipp Maier5e06cee2018-02-01 18:28:08 +01001031 }
1032 setverdict(pass);
1033 }
1034
Harald Weltee636afd2017-09-17 16:24:09 +08001035 /* TODO: various SDP related bits */
1036
1037
1038 /* TODO: CRCX with X-Osmux */
1039 /* TODO: double CRCX without force_realloc */
1040
1041 /* TODO: MDCX (various) */
1042
1043 /* TODO: MDCX without CRCX first */
1044 testcase TC_mdcx_without_crcx() runs on dummy_CT {
1045 var template MgcpCommand cmd;
1046 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001047 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +01001048 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +08001049 var template MgcpResponse rtmpl := {
1050 line := {
1051 /* TODO: accept/enforce better error? */
1052 code := "400",
1053 string := ?
1054 },
1055 params:= { },
1056 sdp := omit
1057 };
1058
Harald Welteedc45c12017-11-18 19:15:05 +01001059 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001060
Harald Welte2bcfd3a2017-11-18 22:14:35 +01001061 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001062 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1063 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1064 valueof(ts_SDP_ptime(20)) });
1065 resp := mgcp_transceive_mgw(cmd, rtmpl);
1066 setverdict(pass);
1067 }
1068
1069 /* DLCX without CRCX first */
1070 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1071 var template MgcpCommand cmd;
1072 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001073 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001074 var template MgcpResponse rtmpl := {
1075 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001076 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001077 string := ?
1078 },
1079 params:= { },
1080 sdp := omit
1081 };
1082
Harald Welteedc45c12017-11-18 19:15:05 +01001083 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001084
Harald Welteedc45c12017-11-18 19:15:05 +01001085 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001086 resp := mgcp_transceive_mgw(cmd, rtmpl);
1087 setverdict(pass);
1088 }
1089
Philipp Maier21c1cff2021-07-20 14:22:53 +02001090 /* DLCX to non existing endpoint */
1091 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1092 var template MgcpCommand cmd;
1093 var MgcpResponse resp;
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001094 var charstring non_existant_ep := hex2str(int2hex(mp_num_endpoints + 1, 4))
1095 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & non_existant_ep & "@" & c_mgw_domain;
Philipp Maier21c1cff2021-07-20 14:22:53 +02001096 var template MgcpResponse rtmpl := {
1097 line := {
1098 code := ("500"),
1099 string := ?
1100 },
1101 params:= { },
1102 sdp := omit
1103 };
1104
1105 f_init(ep);
1106
1107 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1108 resp := mgcp_transceive_mgw(cmd, rtmpl);
1109 setverdict(pass);
1110 }
1111
Philipp Maier8a3dc922018-02-02 14:55:12 +01001112 /* test valid wildcarded MDCX */
1113 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1114 /* Note: A wildcarded MDCX is not allowed, so we expect the
1115 * MGW to reject this request */
1116 var template MgcpCommand cmd;
1117 var MgcpResponse resp;
1118 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1119 var MgcpCallId call_id := '1225'H;
1120 var template MgcpResponse rtmpl := {
1121 line := {
1122 /* TODO: accept/enforce better error? */
1123 code := "507",
1124 string := ?
1125 },
1126 params:= { },
1127 sdp := omit
1128 };
1129
1130 f_init(ep);
1131
1132 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1133 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1134 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1135 valueof(ts_SDP_ptime(20)) });
1136 resp := mgcp_transceive_mgw(cmd, rtmpl);
1137 setverdict(pass);
1138 }
1139
1140 /* test valid wildcarded DLCX */
1141 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001142 var template MgcpCommand cmd;
1143 var MgcpResponse resp;
1144 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001145 var integer i;
1146 var MgcpCallId call_id := '1234'H;
1147 var StatsDExpects expect;
1148 f_init(ep);
1149
1150 /* Allocate a few endpoints */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001151 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier55b90542021-07-02 12:33:19 +02001152 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1153 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1154 }
1155
Philipp Maier1298b092021-11-18 18:33:39 +01001156 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
1157 * occupied endpoints */
1158 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001159 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001160 { 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 +02001161 };
1162 f_statsd_expect(expect);
1163
1164 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001165 var template MgcpResponse rtmpl := {
1166 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001167 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001168 string := ?
1169 },
1170 params:= { },
1171 sdp := omit
1172 };
Philipp Maier55b90542021-07-02 12:33:19 +02001173 cmd := ts_DLCX(get_next_trans_id(), ep);
1174 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001175
Philipp Maier6c740e82022-06-30 12:04:34 +02001176 /* Query a the statsd once to ensure that intermediate results are pulled from the
1177 * pipeline. The second query (below) will return the actual result. */
1178 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001179 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := mp_num_endpoints}
Philipp Maier6c740e82022-06-30 12:04:34 +02001180 };
1181 f_statsd_expect(expect);
1182
1183 /* The second query must resturn a result with 0 endpoints in use. */
Philipp Maier55b90542021-07-02 12:33:19 +02001184 expect := {
1185 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1186 };
1187 f_statsd_expect(expect);
1188
Philipp Maier8a3dc922018-02-02 14:55:12 +01001189 setverdict(pass);
1190 }
1191
Harald Welte79181ff2017-11-18 19:26:11 +01001192 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1193 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1194 var template MgcpCommand cmd;
1195 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001196 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001197 var MgcpCallId call_id := '51234'H;
1198
1199 f_init(ep);
1200
1201 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1202 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1203
1204 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1205
1206 setverdict(pass);
1207 }
1208
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001209 /* test valid CRCX without SDP */
1210 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1211 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1212 setverdict(pass);
1213 }
1214
1215 /* test valid CRCX without SDP */
1216 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1217 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1218 setverdict(pass);
1219 }
1220
1221 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1222 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1223 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1224 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1225 setverdict(pass);
1226 }
1227
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02001228 /* test Creating 257 concurrent osmux conns. It should fail since maximum is 256. */
1229 testcase TC_crcx_osmux_257() runs on dummy_CT {
1230 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1231 var template MgcpCommand cmd;
1232 var MgcpResponse resp;
1233 var charstring cid_response;
1234 var integer i;
1235
1236 f_init(ep, true);
1237
1238 for (i := 0; i < 256; i := i + 1) {
1239
1240 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1241 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
1242 extract_conn_id(resp);
1243
1244 /* extract Osmux CID we got assigned by the MGW */
1245 var MgcpMessage resp_msg := {
1246 response := resp
1247 }
1248
1249 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
1250 setverdict(fail, "No Osmux CID in MGCP response", resp);
1251 mtc.stop;
1252 }
1253
1254 /* Make sure response is no wildcard */
1255 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
1256 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
1257 mtc.stop;
1258 }
1259 }
1260
1261 /* Now conn num 257, it should fail due to all Osmux conns already allocated: */
1262 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1263 resp := mgcp_transceive_mgw(cmd, tr_MgcpResp_Err("400"));
1264
1265 setverdict(pass);
1266
1267 /* Clean up */
1268 for (i := 0; i < 256; i := i + 1) {
1269 f_dlcx_ok(ep, int2hex(i, 4));
1270 }
1271 }
1272
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001273 /* Create one half open connection in receive-only mode. The MGW must accept
1274 * the packets but must not send any. */
1275 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1276 var RtpFlowData flow;
1277 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1278 var MgcpCallId call_id := '1225'H;
1279 var OsmuxemStats stats;
1280 var OsmuxTxHandle tx_hdl;
1281
1282 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001283 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001284 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001285 flow.osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001286 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1287
1288 /* create a transmitter not yet known by MGW */
1289 tx_hdl := valueof(t_TxHandleAMR590(2));
1290 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1291
1292 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1293 f_sleep(1.0);
1294 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1295
1296 stats := f_osmuxem_stats_get(OsmuxEM);
1297
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001298 if (stats.num_pkts_tx < 40 / flow.osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001299 setverdict(fail);
1300 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001301 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 +02001302 setverdict(fail);
1303 }
1304
1305 f_osmuxem_stats_err_check(stats);
1306
1307 setverdict(pass);
1308 }
1309
1310 /* Create one connection in loopback mode, test if the Osmux packets are
1311 * actually reflected */
1312 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1313 var RtpFlowData flow;
1314 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1315 var MgcpCallId call_id := '1225'H;
1316 var OsmuxemStats stats;
1317 var OsmuxTxHandle tx_hdl;
1318
1319 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001320 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001321 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001322 flow.osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001323 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1324
1325 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1326 f_sleep(1.0);
1327
1328 /* Switch off both Tx, wait to receive delayed frames from MGW */
1329 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1330 f_sleep(0.1);
1331 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1332
1333 stats := f_osmuxem_stats_get(OsmuxEM);
1334
1335 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1336 setverdict(fail);
1337 }
1338 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1339 setverdict(fail);
1340 }
1341
1342 f_osmuxem_stats_err_check(stats);
1343
1344 setverdict(pass);
1345 }
1346
1347 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1348 * must match the reception statistics on the other side and vice versa. The
1349 * user may also supply a tolerance value (number of packets) when deviations
1350 * are acceptable */
1351 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1352 var integer plen;
1353
1354 log("stats A: ", a);
1355 log("stats B: ", b);
1356 log("tolerance: ", tolerance, " packets");
1357 log("batch_size: ", batch_size, " packets");
1358
1359 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1360
1361 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1362 return false;
1363 }
1364
1365 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1366 return false;
1367 }
1368
1369 if(a.num_pkts_tx > 0) {
1370 plen := a.bytes_payload_tx / a.num_pkts_tx;
1371 } else {
1372 plen := 0;
1373 }
1374
1375 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1376 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1377 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1378 return false;
1379 }
1380
1381 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) {
1382 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1383 return false;
1384 }
1385
1386 return true;
1387 }
1388
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001389 function f_TC_two_crcx_and_rtp_osmux(boolean bidir, boolean rtp_amr_oa,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001390 charstring local_ip_rtp, charstring remote_ip_rtp,
1391 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001392 var RtpFlowData flow[2];
1393 var RtpemStats stats_rtp;
1394 var OsmuxemStats stats_osmux;
1395 var MgcpResponse resp;
1396 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1397 var MgcpCallId call_id := '1226'H;
1398 var integer tolerance := 0;
1399
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001400 var octetstring amr_payload;
1401 var charstring fmtp;
1402
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001403 f_init(ep, true);
1404
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001405 var AMRFT cmr := AMR_FT_0;
1406 var AMRFT ft := AMR_FT_2;
1407 if (rtp_amr_oa) {
1408 fmtp := "octet-align=1";
1409 var RTP_AMR_Hdr amr_oa_hdr := valueof(ts_RTP_AMR_Hdr(enum2int(cmr), enum2int(ft)));
1410 amr_payload := enc_RTP_AMR_Hdr(amr_oa_hdr) &
1411 f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload);
1412 } else {
1413 fmtp := "octet-align=0";
1414 /* Convert OA to BWE: */
1415 var RTP_AMR_BWE_Hdr amr_bwe_hdr := valueof(ts_RTP_AMR_BWE_Hdr(enum2int(cmr), enum2int(ft)));
1416 var bitstring amr_bwe_hdr_bits := substr(oct2bit(enc_RTP_AMR_BWE_Hdr(amr_bwe_hdr)), 0 , 10);
1417 var bitstring amr_data_bits := oct2bit(f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload));
1418 var bitstring amr_payload_bits := amr_bwe_hdr_bits & substr(amr_data_bits, 0, f_amrft_payload_bits_len(enum2int(ft)));
1419 amr_payload := bit2oct(f_pad_bit(amr_payload_bits, (lengthof(amr_payload_bits)+7)/8*8, '0'B));
1420 };
1421
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001422 /* from us to MGW */
Philipp Maier6b41e152023-03-31 11:45:24 +02001423 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000", fmtp));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001424 flow[0].rtp_cfg := c_RtpemDefaultCfg
Philipp Maierbbe454d2023-03-28 15:31:57 +02001425 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1426 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := amr_payload;
1427 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1428 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := amr_payload;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001429 /* bind local RTP emulation sockets */
1430 flow[0].em.portnr := 10000;
1431 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1432
1433 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001434 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001435 flow[1].em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001436 flow[1].osmux.local_cid := 2;
1437 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001438 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1439
1440 if (bidir) {
1441 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1442 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1443
1444 /* Note: When we test bidirectional we may
1445 * loose packets during switch off because
1446 * both ends are transmitting and we only
1447 * can switch them off one by one. */
1448 tolerance := 3;
1449 } else {
1450 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1451 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1452 }
1453
1454 f_sleep(1.0);
1455
1456 /* Switch off both Tx, wait to receive delayed frames from MGW */
1457 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1458 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1459 f_sleep(0.1);
1460
1461 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1462 f_flow_delete(RTPEM[1]);
1463
1464 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1465 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001466 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 +02001467 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1468 mtc.stop;
1469 }
1470
1471 f_rtpem_stats_err_check(stats_rtp);
1472 f_osmuxem_stats_err_check(stats_osmux);
1473
1474 setverdict(pass);
1475 }
1476
1477 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1478 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001479 f_TC_two_crcx_and_rtp_osmux(false, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001480 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001481 }
1482
1483 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1484 * exchange some data in both directions */
1485 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001486 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001487 mp_local_ipv4, mp_remote_ipv4);
1488 }
1489
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001490 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1491 * exchange some data in both directions. RTP side is configured to
1492 * rx/rx AMR in bandwidth-efficient mode. */
1493 testcase TC_two_crcx_and_rtp_osmux_bidir_amr_bwe() runs on dummy_CT {
1494 f_TC_two_crcx_and_rtp_osmux(true, false, mp_local_ipv4, mp_remote_ipv4,
1495 mp_local_ipv4, mp_remote_ipv4);
1496 }
1497
1498
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001499 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1500 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001501 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001502 mp_local_ipv6, mp_remote_ipv6);
1503 }
1504 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1505 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001506 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001507 mp_local_ipv6, mp_remote_ipv6);
1508 }
1509 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1510 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001511 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001512 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001513 }
1514
1515
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001516 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1517 charstring local_ip_rtp, charstring remote_ip_rtp,
1518 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001519 var RtpFlowData flow[2];
1520 var RtpemStats stats_rtp;
1521 var OsmuxemStats stats_osmux;
1522 var MgcpResponse resp;
1523 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1524 var MgcpCallId call_id := '1227'H;
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001525 var integer num_pkts_tx[2], num_pkts_rx[2];
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001526 var integer temp;
1527
1528 f_init(ep, true);
1529
1530 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001531 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001532 flow[0].rtp_cfg := c_RtpemDefaultCfg
Philipp Maierbbe454d2023-03-28 15:31:57 +02001533 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1534 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001535 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
Philipp Maierbbe454d2023-03-28 15:31:57 +02001536 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);
1537 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 +02001538 /* bind local RTP emulation sockets */
1539 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001540 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001541
1542
1543 /* Create the second connection. This connection will be also
1544 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001545 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001546 flow[1].em.portnr := mp_local_osmux_port;
1547 if (crcx_osmux_wildcard) {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001548 flow[1].osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001549 } else {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001550 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001551 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001552 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001553 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001554
1555
1556 /* The first leg starts transmitting */
1557 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1558 f_sleep(0.5);
1559 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1560 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1561 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1562 mtc.stop;
1563 }
1564 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1565 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1566 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1567 mtc.stop;
1568 }
1569
1570 /* The second leg starts transmitting a little later */
1571 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1572 f_sleep(1.0);
1573 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1574 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1575 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1576 mtc.stop;
1577 }
1578 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1579 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1580 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1581 mtc.stop;
1582 }
1583
1584 /* The first leg will now be switched into bidirectional
1585 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001586 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001587 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001588 /* At this point in time, flow[1](Osmux)->MGW->flow[0](RTP) is active,
1589 * hence if local CID was provided during CRCX we should already be seeing packets
1590 * flowing in one direction, aka stats_rtp.num_pkts_rx sould be >0 after a while: */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001591 f_sleep(0.5);
1592 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1593 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1594 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1595 mtc.stop;
1596 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001597 if (not crcx_osmux_wildcard and stats_rtp.num_pkts_rx == 0) {
1598 setverdict(fail, "received 0 packets Osmux->MGW->RTP");
1599 mtc.stop;
1600 }
1601
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001602 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1603 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1604 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1605 mtc.stop;
1606 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001607 if (stats_osmux.num_pkts_rx > 0) {
1608 setverdict(fail, "received unexpected ", stats_osmux.num_pkts_rx, " packets RTP->MGW->Osmux");
1609 mtc.stop;
1610 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001611
1612 /* When the second leg is switched into bidirectional mode
1613 * as well, then the MGW will connect the two together and
1614 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001615 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001616 if (crcx_osmux_wildcard) {
Pau Espin Pedrol982b9792022-10-07 11:48:13 +02001617 /* We set now the local CID in MDCX: */
1618 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001619 }
1620 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001621 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1622 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1623 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
1624 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
1625 num_pkts_rx[0] := stats_rtp.num_pkts_rx;
1626 num_pkts_rx[1] := stats_osmux.num_pkts_rx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001627 f_sleep(2.0);
1628
1629 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1630 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1631
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001632 temp := (stats_rtp.num_pkts_tx - num_pkts_tx[0]) -
1633 (stats_osmux.num_pkts_rx - num_pkts_rx[1]) * flow[1].osmux.cfg.batch_size;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001634 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 +02001635 log("stats_rtp: ", stats_rtp);
1636 log("stats_osmux: ", stats_osmux);
1637 log("old_rtp_tx: ", num_pkts_tx[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001638 setverdict(fail, "RTP-Tx vs OSmux-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001639 mtc.stop;
1640 }
1641
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001642 temp := (stats_osmux.num_pkts_tx - num_pkts_tx[1]) -
1643 ((stats_rtp.num_pkts_rx - num_pkts_rx[0])/ flow[1].osmux.cfg.batch_size);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001644 if (temp > 3 or temp < -3) {
Pau Espin Pedroldb1e0682022-10-06 19:40:29 +02001645 log("stats_rtp: ", stats_rtp);
1646 log("stats_osmux: ", stats_osmux);
1647 log("old_osmux_tx: ", num_pkts_tx[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001648 setverdict(fail, "Osmux-Tx vs RTP-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001649 mtc.stop;
1650 }
1651
1652 f_rtpem_stats_err_check(stats_rtp);
1653 f_osmuxem_stats_err_check(stats_osmux);
1654
1655 /* Tear down */
1656 f_flow_delete(RTPEM[0]);
1657 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1658 setverdict(pass);
1659 }
1660
1661 /* create one RTP and one OSmux emulations and pass data in both
1662 directions. Create CRCX with wildcard Osmux CID and set it later
1663 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1664 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001665 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1666 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001667 }
1668
1669 /* create one RTP and one OSmux emulations and pass data in both
1670 directions. Create CRCX with fixed Osmux CID and keep it during
1671 MDCX. This is similar to how BSC sets up the call in AoIP. */
1672 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001673 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1674 mp_local_ipv4, mp_remote_ipv4);
1675 }
1676
1677 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1678 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1679 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1680 mp_local_ipv6, mp_remote_ipv6);
1681 }
1682 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1683 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1684 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1685 mp_local_ipv6, mp_remote_ipv6);
1686 }
1687 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1688 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1689 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1690 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001691 }
1692
Harald Welte646ecdb2017-12-28 03:21:57 +01001693 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1694 var template MgcpCommand cmd;
1695 var MgcpResponse resp;
1696
1697 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1698 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1699
1700 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1701
1702 setverdict(pass);
1703 }
1704
1705 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1706 var MgcpEndpoint ep;
1707 var MgcpCallId call_id;
1708 var integer ep_nr;
1709
1710 f_init();
1711
1712 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001713 if(ep_nr > 15) {
1714 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1715 } else {
1716 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1717 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001718 call_id := int2hex(ep_nr, 2) & '1234'H;
1719 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1720 }
1721 }
1722
Harald Welte79181ff2017-11-18 19:26:11 +01001723 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1724 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001725 var template MgcpCommand cmd;
1726 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001727 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001728 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001729
Harald Welteedc45c12017-11-18 19:15:05 +01001730 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001731
Harald Welteba62c8c2017-11-18 18:26:49 +01001732 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001733 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001734
Harald Welteba62c8c2017-11-18 18:26:49 +01001735 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001736
1737 setverdict(pass);
1738 }
1739
Harald Welte79181ff2017-11-18 19:26:11 +01001740 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1741 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1742 var template MgcpCommand cmd;
1743 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001744 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001745 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001746
1747 f_init(ep);
1748
1749 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1750 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1751
1752 f_dlcx_ok(ep);
1753
1754 setverdict(pass);
1755 }
1756
1757
Harald Welte6d167f82017-11-18 19:41:35 +01001758 /* CRCX + DLCX of valid endpoint but invalid call-id */
1759 testcase TC_crcx_and_dlcx_ep_callid_inval() 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 := '51231'H;
1764
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(ep, "516", *, 'ffff'H);
1771
1772 setverdict(pass);
1773 }
1774
1775
1776 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1777 testcase TC_crcx_and_dlcx_ep_callid_connid_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 := '51230'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, "515", *, call_id, 'ffff'H);
1789
1790 setverdict(pass);
1791 }
1792
1793
Harald Weltee636afd2017-09-17 16:24:09 +08001794 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001795 testcase TC_crcx_and_dlcx_retrans() 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 Weltef53f1642017-11-18 19:57:11 +01001799 var MgcpCallId call_id := '51229'H;
1800 var template MgcpResponse rtmpl := {
1801 line := {
1802 code := "200",
1803 string := "OK"
1804 },
1805 params:= { },
1806 sdp := omit
1807 };
1808
1809 f_init(ep);
1810
1811 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1812 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1813
1814 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1815 resp := mgcp_transceive_mgw(cmd, rtmpl);
1816 resp := mgcp_transceive_mgw(cmd, rtmpl);
1817
1818 setverdict(pass);
1819 }
1820
Harald Weltebb7523b2018-03-29 08:52:01 +02001821 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1822 testcase TC_rtpem_selftest() runs on dummy_CT {
1823 var RtpemStats stats[2];
1824 var integer local_port := 10000;
1825 var integer local_port2 := 20000;
1826
1827 f_init();
1828
1829 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1830 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1831
1832 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1833 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1834
1835 log("=== starting");
1836 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1837 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1838
1839 f_sleep(5.0);
1840
1841 log("=== stopping");
1842 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1843 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1844 f_sleep(0.5);
1845 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1846 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1847
1848 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1849 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1850 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1851 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001852 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001853 }
1854 setverdict(pass);
1855 }
1856
Philipp Maier2321ef92018-06-27 17:52:04 +02001857 /* Create one half open connection in receive-only mode. The MGW must accept
1858 * the packets but must not send any. */
1859 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1860 var RtpFlowData flow;
1861 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1862 var MgcpCallId call_id := '1225'H;
1863 var RtpemStats stats;
1864
1865 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001866 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001867 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001868 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001869
1870 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1871 f_sleep(1.0);
1872 f_flow_delete(RTPEM[0], ep, call_id);
1873
1874 stats := f_rtpem_stats_get(RTPEM[0]);
1875
Philipp Maier42b17cc2019-10-01 13:53:17 +02001876 /* Make sure that at least some amount of RTP packets/bytes
1877 * have has been transmitted. The compare values for
1878 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1879 * using a testrun and the results were devided by 2, so even
1880 * in load situations we should reach the minimum amount of
1881 * required packets/bytes */
1882
1883 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001884 setverdict(fail);
1885 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001886 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001887 setverdict(fail);
1888 }
Philipp Maier36291392018-07-25 09:40:44 +02001889
1890 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001891
1892 setverdict(pass);
1893 }
1894
1895 /* Create one connection in loopback mode, test if the RTP packets are
1896 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001897 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 +02001898 var RtpFlowData flow;
1899 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1900 var MgcpCallId call_id := '1225'H;
1901 var RtpemStats stats;
1902
1903 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001904 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001905 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001906 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001907
1908 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1909 f_sleep(1.0);
1910 f_flow_delete(RTPEM[0], ep, call_id);
1911
1912 stats := f_rtpem_stats_get(RTPEM[0]);
1913
1914 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1915 setverdict(fail);
1916 }
1917 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1918 setverdict(fail);
1919 }
Philipp Maier36291392018-07-25 09:40:44 +02001920
1921 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001922
1923 setverdict(pass);
1924 }
1925
Philipp Maier1ac13982021-05-07 23:06:07 +02001926 /* Create one connection in loopback mode, test if the RTP packets are
1927 * actually reflected */
1928 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001929 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001930 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001931 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1932 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1933 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001934
1935 /* Same as above, but we will intenionally not tell the MGW where to
1936 * send the outgoing traffic. The connection is still created in
1937 * loopback mode, so the MGW should take the originating address from
1938 * the incoming RTP packet and send it back to the source */
1939 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001940 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001941 }
1942
1943
Philipp Maier7df85f62018-07-25 10:26:09 +02001944 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1945 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001946 var RtpFlowData flow[2];
1947 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001948 var MgcpResponse resp;
1949 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1950 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001951 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001952
1953 f_init(ep);
1954
1955 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001956 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001957 /* bind local RTP emulation sockets */
1958 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001959 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001960
1961 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001962 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001963 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001964 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1965
1966 if (bidir) {
1967 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1968 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1969
1970 /* Note: When we test bidirectional we may
1971 * loose packets during switch off because
1972 * both ends are transmitting and we only
1973 * can switch them off one by one. */
1974 tolerance := 3;
1975 } else {
1976 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1977 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1978 }
1979
1980 f_sleep(1.0);
1981
1982 f_flow_delete(RTPEM[1]);
1983 f_flow_delete(RTPEM[0], ep, call_id);
1984
1985 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1986 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1987 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1988 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001989 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001990 }
1991
Philipp Maier36291392018-07-25 09:40:44 +02001992 f_rtpem_stats_err_check(stats[0]);
1993 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001994
Philipp Maier2321ef92018-06-27 17:52:04 +02001995 setverdict(pass);
1996 }
1997
1998 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1999 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02002000 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02002001 }
2002
2003 /* create two local RTP emulations; create two connections on MGW EP,
2004 * exchange some data in both directions */
2005 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02002006 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
2007 }
2008
2009 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
2010 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
2011 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
2012 }
2013
2014 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
2015 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
2016 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02002017 }
2018
2019 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002020 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002021 charstring local_ip_b, charstring remote_ip_b,
2022 uint7_t payload_type := 3,
2023 charstring codec_name := "GSM/8000/1") runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02002024 var RtpFlowData flow[2];
2025 var RtpemStats stats[2];
2026 var MgcpResponse resp;
2027 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2028 var MgcpCallId call_id := '1227'H;
2029 var integer num_pkts_tx[2];
2030 var integer temp;
2031
2032 f_init(ep);
2033
2034 /* Create the first connection in receive only mode */
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002035 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, payload_type, codec_name));
Philipp Maier2321ef92018-06-27 17:52:04 +02002036 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002037 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02002038
2039 /* Create the second connection. This connection will be also
2040 * in receive only mode */
Oliver Smith3cfa2cc2023-01-24 13:50:26 +01002041 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, payload_type, codec_name));
Philipp Maier2321ef92018-06-27 17:52:04 +02002042 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002043 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02002044
2045 /* The first leg starts transmitting */
2046 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2047 f_sleep(0.5);
2048 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2049 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002050 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002051 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002052 }
2053 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2054 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002055 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002056 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002057 }
2058
2059 /* The second leg starts transmitting a little later */
2060 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2061 f_sleep(1.0);
2062 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2063 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002064 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002065 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002066 }
2067 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2068 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002069 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002070 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002071 }
2072
2073 /* The first leg will now be switched into bidirectional
2074 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002075 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2076 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2077 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002078 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2079 f_sleep(0.5);
2080 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002081 if (stats[0].num_pkts_rx_err_disabled != 0) {
2082 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 /* When the second leg is switched into bidirectional mode
2092 * as well, then the MGW will connect the two together and
2093 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02002094 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2095 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002096 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002097 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2098 f_sleep(2.0);
2099
2100 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2101 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2102
2103 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2104 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002105 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002106 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002107 }
2108
2109 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2110 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002111 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002112 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002113 }
Philipp Maier36291392018-07-25 09:40:44 +02002114
2115 f_rtpem_stats_err_check(stats[0]);
2116 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02002117
2118 /* Tear down */
2119 f_flow_delete(RTPEM[0]);
2120 f_flow_delete(RTPEM[1], ep, call_id);
2121 setverdict(pass);
2122 }
2123
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002124 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
2125 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2126 mp_local_ipv4, mp_remote_ipv4);
2127 }
2128
2129 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
2130 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
2131 mp_local_ipv6, mp_remote_ipv6);
2132 }
2133
2134 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
2135 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2136 mp_local_ipv6, mp_remote_ipv6);
2137 }
2138
Oliver Smith871f45a2023-01-24 16:21:50 +01002139 testcase TC_two_crcx_mdcx_and_rtp_clearmode() runs on dummy_CT {
2140 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2141 mp_local_ipv4, mp_remote_ipv4,
2142 120, /* 3GPP TS 48.103 table 5.4.2.2.1 */
2143 "CLEARMODE/8000");
2144 }
2145
Philipp Maier2321ef92018-06-27 17:52:04 +02002146 /* Test what happens when two RTP streams from different sources target
2147 * a single connection. Is the unsolicited stream properly ignored? */
2148 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2149 var RtpFlowData flow[2];
2150 var RtpemStats stats[2];
2151 var MgcpResponse resp;
2152 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2153 var MgcpCallId call_id := '1234321326'H;
2154 var integer unsolicited_port := 10002;
2155
2156 f_init(ep);
2157
2158 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002159 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002160 /* bind local RTP emulation sockets */
2161 flow[0].em.portnr := 10000;
2162 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2163
2164 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002165 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002166 flow[1].em.portnr := 20000;
2167 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002168
2169 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2170 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2171
Philipp Maier2321ef92018-06-27 17:52:04 +02002172 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002173
Philipp Maier2321ef92018-06-27 17:52:04 +02002174 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002175 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2176 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002177 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2178
2179 f_sleep(0.5);
2180
Daniel Willmanna069d382018-12-13 13:53:33 +01002181 /* Stop transmitting packets and tear down the flows */
2182 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002183 f_flow_delete(RTPEM[0]);
2184 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002185
2186 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2187 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2188 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2189 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002190 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002191 }
2192
Philipp Maier36291392018-07-25 09:40:44 +02002193 f_rtpem_stats_err_check(stats[0]);
2194 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002195
Harald Weltebb7523b2018-03-29 08:52:01 +02002196 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002197 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002198
Philipp Maier2321ef92018-06-27 17:52:04 +02002199 /* Test a handover situation. We first create two connections transmit
2200 * some data bidirectionally. Then we will simulate a handover situation. */
2201 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2202 var RtpFlowData flow[2];
2203 var RtpemStats stats[3];
2204 var MgcpResponse resp;
2205 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2206 var MgcpCallId call_id := '76338'H;
2207 var integer port_old;
2208
2209 f_init(ep);
2210
2211 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002212 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002213 /* bind local RTP emulation sockets */
2214 flow[0].em.portnr := 10000;
2215 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2216
2217 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002218 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002219 flow[1].em.portnr := 20000;
2220 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2221
2222 /* Normal rtp flow for one second */
2223 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2224 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2225 f_sleep(1.0);
2226
2227 /* Now switch the flow over to a new port (BTS) */
2228 port_old := flow[0].em.portnr;
2229 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002230 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002231 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002232 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002233
2234 /* When handing over a call, the old source may still keep
2235 * transmitting for a while. We simulate this by injecting
2236 * some unsolicited packets on the behalf of the old source,
2237 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002238 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2239 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002240 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2241 f_sleep(1.0);
2242 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2243 f_sleep(1.0);
2244
2245 /* Terminate call */
2246 f_flow_delete(RTPEM[0]);
2247 f_flow_delete(RTPEM[1], ep, call_id);
2248
2249 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2250 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2251 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2252 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002253 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002254 }
2255 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2256 if (stats[2].num_pkts_rx_err_disabled != 0) {
2257 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002258 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002259 }
2260
Philipp Maier36291392018-07-25 09:40:44 +02002261 f_rtpem_stats_err_check(stats[0]);
2262 f_rtpem_stats_err_check(stats[1]);
2263 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002264
Philipp Maier2321ef92018-06-27 17:52:04 +02002265 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002266 }
Harald Weltef53f1642017-11-18 19:57:11 +01002267
Philipp Maier6d4e0942019-02-21 17:35:01 +01002268
2269 /* create two local RTP emulations; create two connections on MGW EP, see if
Philipp Maier8ed48c52023-02-07 11:24:31 +01002270 * exchanged data is converted between ts101318 and rfc5993 */
2271 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 +01002272 var RtpFlowData flow[2];
2273 var RtpemStats stats[2];
2274 var MgcpResponse resp;
2275 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2276 var MgcpCallId call_id := '1226'H;
2277
2278 f_init(ep);
2279
2280 /* Turn on conversion mode */
2281 f_vty_enter_config(MGWVTY);
2282 f_vty_transceive(MGWVTY, "mgcp");
2283 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2284
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002285 /* Connection #0 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002286 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000", fmtp0));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002287 /* bind local RTP emulation sockets */
2288 flow[0].em.portnr := 10000;
2289 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002290 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2291 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2292 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0;
2293 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002294 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2295
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002296 /* Connection #1 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002297 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000", fmtp1));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002298 flow[1].em.portnr := 20000;
2299 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002300 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2301 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2302 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := pl1;
2303 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := pl1;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002304 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2305
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002306 /* Send RTP packets to connection #0, receive on connection #1 */
2307 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2308 f_sleep(0.5);
2309 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002310 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002311 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2312 f_sleep(0.5);
2313 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002314
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002315 /* Send RTP packets to connection #1, receive on connection #0 */
2316 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2317 f_sleep(0.5);
2318 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2319 f_sleep(1.0);
2320 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2321 f_sleep(0.5);
2322 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2323
2324 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002325 f_flow_delete(RTPEM[0]);
2326 f_flow_delete(RTPEM[1], ep, call_id);
2327
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002328 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002329 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2330 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002331 f_rtpem_stats_err_check(stats[0]);
2332 f_rtpem_stats_err_check(stats[1]);
2333
2334 /* Turn off conversion mode */
2335 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2336
2337 setverdict(pass);
2338 }
2339
Philipp Maier8ed48c52023-02-07 11:24:31 +01002340 const octetstring rtp_hr_gsm_ts101318 := '0b11b3eede60be4e3ec68838c7b5'O;
2341 const octetstring rtp_hr_gsm_rfc5993 := '000b11b3eede60be4e3ec68838c7b5'O;
2342
2343 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2344 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "", "");
2345 }
2346
2347 testcase TC_ts101318_rfc5993_rtp_conversion_fmtp() runs on dummy_CT {
2348 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "gsm-hr-format=ts101318", "gsm-hr-format=rfc5993");
2349 }
2350
Philipp Maier4f764ce2019-03-07 10:54:10 +01002351 /* create two local RTP emulations; create two connections on MGW EP, see if
Philipp Maiereb5e8132023-04-13 16:20:37 +02002352 * exchanged data is converted between AMR octet-aligned and bandwidth
Philipp Maier4f764ce2019-03-07 10:54:10 +01002353 * efficient-mode */
Philipp Maier71484282023-04-04 18:00:53 +02002354 function f_TC_amr_x_x_rtp_conversion(charstring fmtp0, octetstring pl0,
2355 charstring fmtp1a, octetstring pl1a,
2356 charstring fmtp1b, octetstring pl1b) runs on dummy_CT {
Philipp Maier4f764ce2019-03-07 10:54:10 +01002357 var RtpFlowData flow[2];
2358 var RtpemStats stats[2];
2359 var MgcpResponse resp;
2360 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2361 var MgcpCallId call_id := '1226'H;
2362
2363 f_init(ep);
2364
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002365 /* Connection #0 (Bidirectional) */
Philipp Maier6b41e152023-03-31 11:45:24 +02002366 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 96, "AMR/8000", fmtp0));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002367 /* bind local RTP emulation sockets */
2368 flow[0].em.portnr := 10000;
2369 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002370 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2371 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2372 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0;
2373 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0;
Philipp Maier4f764ce2019-03-07 10:54:10 +01002374 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2375
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002376 /* Connection #1 (Bidirectional) */
Philipp Maier71484282023-04-04 18:00:53 +02002377 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000", fmtp1a));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002378 flow[1].em.portnr := 20000;
2379 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maier71484282023-04-04 18:00:53 +02002380 flow[1].rtp_cfg.rx_payloads := {};
2381 flow[1].rtp_cfg.tx_payloads := {};
2382 if (pl1a != ''O) {
2383 flow[1].rtp_cfg.rx_payloads := flow[1].rtp_cfg.rx_payloads & {{112, pl1a}};
2384 flow[1].rtp_cfg.tx_payloads := flow[1].rtp_cfg.tx_payloads & {{112, pl1a}};
2385 }
2386
2387 /* The second fmtp parameter is to simulate a call agent that offers the transmission both modes. */
2388 if (fmtp1b != "") {
2389 flow[1].codec_descr := flow[1].codec_descr & {{113, "AMR/8000", fmtp1b}};
2390 if (pl1b != ''O) {
2391 flow[1].rtp_cfg.rx_payloads := flow[1].rtp_cfg.rx_payloads & {{113, pl1b}};
2392 flow[1].rtp_cfg.tx_payloads := flow[1].rtp_cfg.tx_payloads & {{113, pl1b}};
2393 }
2394 }
2395
Philipp Maier4f764ce2019-03-07 10:54:10 +01002396 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2397
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002398 /* Send RTP packets to connection #0, receive on connection #1 */
2399 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2400 f_sleep(0.5);
2401 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002402 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002403 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2404 f_sleep(0.5);
2405 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002406
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002407 /* Send RTP packets to connection #1, receive on connection #0 */
2408 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2409 f_sleep(0.5);
2410 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2411 f_sleep(1.0);
2412 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2413 f_sleep(0.5);
2414 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2415
2416 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002417 f_flow_delete(RTPEM[0]);
2418 f_flow_delete(RTPEM[1], ep, call_id);
2419
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002420 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002421 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2422 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002423 f_rtpem_stats_err_check(stats[0]);
2424 f_rtpem_stats_err_check(stats[1]);
2425
2426 setverdict(pass);
2427 }
2428
Philipp Maier882843d2020-05-25 15:33:13 +02002429 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2430 * functions are real world AMR RTP payloads including AMR header. The
2431 * payloads were extracted from a trace with known good payloads. */
2432
Philipp Maiercd756ed2023-02-13 11:12:24 +01002433 const octetstring rtp_amr_5_90k_oa := '2014e959f35fdfe5e9667ffbc088818088'O;
2434 const octetstring rtp_amr_5_90k_bwe := '217a567cd7f7f97a599ffef022206022'O;
2435 const octetstring rtp_amr_5_15k_oa := '100c4e9ba850e30d5d53d04de41e7c'O;
2436 const octetstring rtp_amr_5_15k_bwe := '10d3a6ea1438c35754f41379079f'O;
2437
Philipp Maier71484282023-04-04 18:00:53 +02002438 /* Only one codec on each side */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002439 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier71484282023-04-04 18:00:53 +02002440 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 +01002441 }
Philipp Maier4f764ce2019-03-07 10:54:10 +01002442 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
Philipp Maier71484282023-04-04 18:00:53 +02002443 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa, "octet-align=1", rtp_amr_5_15k_oa, "", ''O);
2444 }
2445 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2446 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 +01002447 }
2448
Philipp Maier71484282023-04-04 18:00:53 +02002449 /* Only one codec on one side, two codecs (compatibility) on other side. The payloads are the same on both
2450 * sides, so the expectation is that conversion must not be performed. Each test is done with both formats in
2451 * two different configurations.*/
2452 testcase TC_amr_oa_oa_no_bwe_rtp_conversion() runs on dummy_CT {
2453 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa,
2454 "octet-align=1", rtp_amr_5_15k_oa,
2455 "octet-align=0", ''O); /* We expect to see NO bandwidth efficient packets! */
2456 }
2457 testcase TC_amr_oa_no_bwe_oa_rtp_conversion() runs on dummy_CT {
2458 /* (Same as above but flipped on the opposite side) */
2459 f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa,
2460 "octet-align=0", ''O, /* We expect to see NO bandwidth efficient packets! */
2461 "octet-align=1", rtp_amr_5_15k_oa);
2462 }
2463 testcase TC_amr_bwe_bwe_no_oa_rtp_conversion() runs on dummy_CT {
2464 f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe,
2465 "octet-align=0", rtp_amr_5_15k_bwe,
2466 "octet-align=1", ''O); /* We expect to see NO octet aligned packets! */
2467 }
2468 testcase TC_amr_bwe_no_oa_bwe_rtp_conversion() runs on dummy_CT {
2469 /* (Same as above but flipped on the opposite side) */
2470 f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe,
2471 "octet-align=1", ''O, /* We expect to see NO octet aligned packets! */
2472 "octet-align=0", rtp_amr_5_15k_bwe);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002473 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002474
Harald Weltee636afd2017-09-17 16:24:09 +08002475 /* TODO: Double-DLCX (no retransmission) */
2476
2477
2478
2479 /* TODO: AUEP (various) */
2480 /* TODO: RSIP (various) */
2481 /* TODO: RQNT (various) */
2482 /* TODO: EPCF (various) */
2483 /* TODO: AUCX (various) */
2484 /* TODO: invalid verb (various) */
2485
Oliver Smith021141e2019-06-25 12:09:01 +02002486
2487 testcase TC_conn_timeout() runs on dummy_CT {
2488 var RtpFlowData flow;
2489 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2490 var MgcpCallId call_id := '1225'H;
2491 var MGCP_RecvFrom mrf;
2492
2493 f_init(ep);
2494 log("Setting conn-timeout to 1s");
2495 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2496
2497 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002498 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002499 flow.em.portnr := 10000;
2500 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2501 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2502 f_sleep(1.5);
2503
2504 log("Stopping for 0.5s and resuming");
2505 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2506 f_sleep(0.5);
2507 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2508 f_sleep(0.1);
2509
2510 log("Stopping for 1.5s, expecting to run into timeout");
2511 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2512 f_sleep(1.5);
2513
2514 log("Resuming should fail now");
2515 f_rtpem_conn_refuse_expect(RTPEM[0]);
2516 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2517 f_sleep(0.2);
2518 f_rtpem_conn_refuse_verify(RTPEM[0]);
2519
2520 setverdict(pass);
2521 }
2522
Philipp Maier2609c752020-07-08 12:38:09 +02002523 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2524 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2525 var template MgcpCommand cmd;
2526 var MgcpResponse resp;
2527 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2528 var MgcpCallId call_id := '8376F297'H;
2529
2530 f_init(ep);
2531
2532 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2533 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2534
2535 f_dlcx_ok(ep);
2536
2537 setverdict(pass);
2538 }
2539
2540 /* Test what happens when overlapping endpoints are selected (E1) */
2541 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2542 var template MgcpCommand cmd;
2543 var MgcpResponse resp;
2544 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2545 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2546 var MgcpCallId call_id_1 := '8376F297'H;
2547 var MgcpCallId call_id_2 := '837AF2A7'H;
2548
2549 f_init();
2550
2551 /* ep_1 and ep_2 are overlapping, selecting both one after
2552 * another should work fine: */
2553 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2554 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2555 f_dlcx_ok(ep_1);
2556 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2557 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2558 f_dlcx_ok(ep_2);
2559
2560 /* When ep_1 is serving a call we can not select ep_2 becaus
2561 * it is overlapping with ep_1 */
2562 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2563 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2564 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2565 resp := mgcp_transceive_mgw(cmd, ?);
2566 if (resp.line.code != "501") {
2567 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2568 }
2569 f_dlcx_ok(ep_1);
2570
2571 setverdict(pass);
2572 }
2573
2574 /* Create one connection in loopback mode, test if the RTP packets are
2575 * actually reflected */
2576 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2577 var RtpFlowData flow;
2578 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2579 var MgcpCallId call_id := '12250989'H;
2580 var RtpemStats stats;
2581
2582 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002583 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002584 flow.em.portnr := 10000;
2585 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2586
2587 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2588 f_sleep(1.0);
2589 f_flow_delete(RTPEM[0], ep, call_id);
2590
2591 stats := f_rtpem_stats_get(RTPEM[0]);
2592
2593 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2594 setverdict(fail);
2595 }
2596 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2597 setverdict(fail);
2598 }
2599
2600 f_rtpem_stats_err_check(stats);
2601
2602 setverdict(pass);
2603 }
2604
Philipp Maier13aff992022-06-30 16:19:59 +02002605 /* test valid wildcarded DLCX on an E1 trunk */
2606 testcase TC_e1_dlcx_wildcarded() runs on dummy_CT {
2607 var template MgcpCommand cmd;
2608 var MgcpEndpoint ep;
2609 var MgcpCallId call_id := '8376F297'H;
2610 var integer n_e1_ts := 4;
2611 var StatsDExpects expect;
2612
2613 f_init();
2614
2615 /* Open a few E1 timeslots */
2616 for (var integer i := 0; i < n_e1_ts; i := i+1) {
2617 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-0@" & c_mgw_domain;
2618 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2619 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2620 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-2@" & c_mgw_domain;
2621 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2622 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2623 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-4@" & c_mgw_domain;
2624 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2625 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2626 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-6@" & c_mgw_domain;
2627 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2628 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2629 }
2630
2631 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
2632 * occupied endpoints */
2633 f_sleep(1.0)
2634 expect := {
2635 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := n_e1_ts * 4, max := n_e1_ts * 4}
2636 };
2637 f_statsd_expect(expect);
2638
2639 /* Send wildcarded DLCX */
2640 var template MgcpResponse rtmpl := {
2641 line := {
2642 code := "200",
2643 string := ?
2644 },
2645 params:= { },
2646 sdp := omit
2647 };
2648 ep := "ds/e1-1/*@" & c_mgw_domain;
2649 cmd := ts_DLCX(get_next_trans_id(), ep);
2650 mgcp_transceive_mgw(cmd, rtmpl);
2651
2652 /* Query a the statsd once to ensure that intermediate results are pulled from the
2653 * pipeline. The second query (below) will return the actual result. */
2654 expect := {
2655 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := n_e1_ts * 4}
2656 };
2657 f_statsd_expect(expect);
2658
2659 /* The second query must resturn a result with 0 endpoints in use. */
2660 expect := {
2661 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := 0}
2662 };
2663 f_statsd_expect(expect);
2664
2665 setverdict(pass);
2666 }
2667
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002668 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2669 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2670 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2671 var template MgcpCommand cmd;
2672 var MgcpResponse resp;
2673 var MgcpCallId call_id := '1234'H;
2674 var MgcpConnectionId conn_id;
2675
2676 f_init(ep);
2677
2678 /* create the connection on the MGW */
2679 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2680 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2681 conn_id := extract_conn_id(resp);
2682
2683 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2684 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2685 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2686 valueof(ts_SDP_ptime(20)) });
2687 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2688
2689 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2690 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2691 }
2692 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2693 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2694 }
2695
2696 /* clean-up */
2697 f_dlcx_ok(ep, call_id);
2698 setverdict(pass);
2699 }
2700
2701 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2702 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2703 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2704 var template MgcpCommand cmd;
2705 var MgcpResponse resp;
2706 var MgcpCallId call_id := '1234'H;
2707 var MgcpConnectionId conn_id;
2708
2709 f_init(ep);
2710
2711 /* create the connection on the MGW */
2712 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2713 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2714 conn_id := extract_conn_id(resp);
2715
2716 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2717 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2718 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2719 valueof(ts_SDP_ptime(20)) });
2720 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2721
2722 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2723 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2724 }
2725 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2726 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2727 }
2728
2729 /* clean-up */
2730 f_dlcx_ok(ep, call_id);
2731 setverdict(pass);
2732 }
2733
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002734 /* create two local RTP+IuUP emulations and pass data in both directions */
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002735 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002736 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002737 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2738 var RtpFlowData flow[2];
2739 var RtpemStats stats[2];
2740 var MgcpResponse resp;
2741 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2742 var MgcpCallId call_id := '1227'H;
2743 var integer num_pkts_tx[2];
2744 var integer temp;
2745
2746 f_init(ep);
2747
2748 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2749 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2750 flow[0].em.portnr := 10000;
2751 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002752 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2753 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002754 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002755 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002756 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002757 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2758 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2759
2760 /* Create the second connection. This connection will be also
2761 * in receive only mode (CN side, IuUP-Init passive) */
2762 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2763 flow[1].em.portnr := 20000;
2764 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002765 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2766 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002767 flow[1].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002768 flow[1].rtp_cfg.iuup_cfg.active_init := false;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002769 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2770 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2771
2772 /* The first leg starts transmitting */
2773 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2774 f_sleep(0.5);
2775 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2776 if (stats[0].num_pkts_rx_err_disabled != 0) {
2777 setverdict(fail, "received packets from MGW on recvonly connection 0");
2778 mtc.stop;
2779 }
2780 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2781 if (stats[1].num_pkts_rx_err_disabled != 0) {
2782 setverdict(fail, "received packets from MGW on recvonly connection 1");
2783 mtc.stop;
2784 }
2785
2786 /* The second leg starts transmitting a little later */
2787 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2788 f_sleep(1.0);
2789 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2790 if (stats[0].num_pkts_rx_err_disabled != 0) {
2791 setverdict(fail, "received packets from MGW on recvonly connection 0");
2792 mtc.stop;
2793 }
2794 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2795 if (stats[1].num_pkts_rx_err_disabled != 0) {
2796 setverdict(fail, "received packets from MGW on recvonly connection 1");
2797 mtc.stop;
2798 }
2799
2800 /* The first leg will now be switched into bidirectional
2801 * mode, but we do not expect any data coming back yet. */
2802 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2803 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2804 num_pkts_tx[1] := stats[1].num_pkts_tx;
2805 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2806 f_sleep(0.5);
2807 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2808 if (stats[0].num_pkts_rx_err_disabled != 0) {
2809 setverdict(fail, "received packets from MGW on recvonly connection 0");
2810 mtc.stop;
2811 }
2812 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2813 if (stats[1].num_pkts_rx_err_disabled != 0) {
2814 setverdict(fail, "received packets from MGW on recvonly connection 1");
2815 mtc.stop;
2816 }
2817
2818 /* When the second leg is switched into bidirectional mode
2819 * as well, then the MGW will connect the two together and
2820 * we should see RTP streams passing through from both ends. */
2821 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2822 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2823 num_pkts_tx[0] := stats[0].num_pkts_tx;
2824 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2825 f_sleep(2.0);
2826
2827 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2828 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2829
2830 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2831 if (temp > 3 or temp < -3) {
2832 setverdict(fail, "number of packets not within normal parameters:", temp);
2833 mtc.stop;
2834 }
2835
2836 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2837 if (temp > 3 or temp < -3) {
2838 setverdict(fail, "number of packets not within normal parameters:", temp);
2839 mtc.stop;
2840 }
2841
2842 f_rtpem_stats_err_check(stats[0]);
2843 f_rtpem_stats_err_check(stats[1]);
2844
2845 /* Tear down */
2846 f_flow_delete(RTPEM[0]);
2847 f_flow_delete(RTPEM[1], ep, call_id);
2848 setverdict(pass);
2849 }
2850 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002851 var template (value) IuUP_RabFlowCombinationList rfcl := {
2852 t_IuUP_RFC_AMR_12_2(0),
2853 t_IuUP_RFC_AMR_SID(1),
2854 t_IuUP_RFC_AMR_NO_DATA(2)
2855 };
2856 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2857 mp_local_ipv4, mp_remote_ipv4);
2858 }
2859 /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */
2860 testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT {
2861 var template (value) IuUP_RabFlowCombinationList rfcl := {
2862 t_IuUP_RFC_AMR_12_2(1),
2863 t_IuUP_RFC_AMR_SID(2),
2864 t_IuUP_RFC_AMR_NO_DATA(0)
2865 };
2866 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002867 mp_local_ipv4, mp_remote_ipv4);
2868 }
2869
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002870 /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */
2871 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 +02002872 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002873 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2874 var RtpFlowData flow[2];
2875 var RtpemStats stats[2];
2876 var MgcpResponse resp;
2877 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2878 var MgcpCallId call_id := '1227'H;
2879 var integer num_pkts_tx[2];
2880 var integer temp;
2881
2882 f_init(ep);
2883
2884 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2885 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2886 flow[0].em.portnr := 10000;
2887 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002888 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2889 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2890 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
2891 /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
2892 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002893 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002894 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002895 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002896 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2897 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2898
2899 /* Create the second connection. This connection will be also
2900 * in receive only mode (CN side, regular RTP) */
2901 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
2902 flow[1].em.portnr := 20000;
2903 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002904 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2905 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2906 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
2907 /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
2908 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002909 flow[1].rtp_cfg.iuup_mode := false;
2910 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2911 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2912
2913 /* The first leg starts transmitting */
2914 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2915 f_sleep(0.5);
2916 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2917 if (stats[0].num_pkts_rx_err_disabled != 0) {
2918 setverdict(fail, "received packets from MGW on recvonly connection 0");
2919 mtc.stop;
2920 }
2921 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2922 if (stats[1].num_pkts_rx_err_disabled != 0) {
2923 setverdict(fail, "received packets from MGW on recvonly connection 1");
2924 mtc.stop;
2925 }
2926
2927 /* The second leg starts transmitting a little later */
2928 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2929 f_sleep(1.0);
2930 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2931 if (stats[0].num_pkts_rx_err_disabled != 0) {
2932 setverdict(fail, "received packets from MGW on recvonly connection 0");
2933 mtc.stop;
2934 }
2935 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2936 if (stats[1].num_pkts_rx_err_disabled != 0) {
2937 setverdict(fail, "received packets from MGW on recvonly connection 1");
2938 mtc.stop;
2939 }
2940
2941 /* The first leg will now be switched into bidirectional
2942 * mode, but we do not expect any data coming back yet. */
2943 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2944 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2945 num_pkts_tx[1] := stats[1].num_pkts_tx;
2946 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2947 f_sleep(0.5);
2948 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2949 if (stats[0].num_pkts_rx_err_disabled != 0) {
2950 setverdict(fail, "received packets from MGW on recvonly connection 0");
2951 mtc.stop;
2952 }
2953 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2954 if (stats[1].num_pkts_rx_err_disabled != 0) {
2955 setverdict(fail, "received packets from MGW on recvonly connection 1");
2956 mtc.stop;
2957 }
2958
2959 /* When the second leg is switched into bidirectional mode
2960 * as well, then the MGW will connect the two together and
2961 * we should see RTP streams passing through from both ends. */
2962 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2963 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2964 num_pkts_tx[0] := stats[0].num_pkts_tx;
2965 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2966 f_sleep(2.0);
2967
2968 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2969 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2970
2971 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2972 if (temp > 3 or temp < -3) {
2973 setverdict(fail, "number of packets not within normal parameters:", temp);
2974 mtc.stop;
2975 }
2976
2977 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2978 if (temp > 3 or temp < -3) {
2979 setverdict(fail, "number of packets not within normal parameters:", temp);
2980 mtc.stop;
2981 }
2982
2983 f_rtpem_stats_err_check(stats[0]);
2984 f_rtpem_stats_err_check(stats[1]);
2985
2986 /* Tear down */
2987 f_flow_delete(RTPEM[0]);
2988 f_flow_delete(RTPEM[1], ep, call_id);
2989 setverdict(pass);
2990 }
2991 testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002992 var template (value) IuUP_RabFlowCombinationList rfcl := {
2993 t_IuUP_RFC_AMR_12_2(0),
2994 t_IuUP_RFC_AMR_SID(1),
2995 t_IuUP_RFC_AMR_NO_DATA(2)
2996 };
2997 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2998 mp_local_ipv4, mp_remote_ipv4);
2999 }
3000 /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */
3001 testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT {
3002 var template (value) IuUP_RabFlowCombinationList rfcl := {
3003 t_IuUP_RFC_AMR_12_2(1),
3004 t_IuUP_RFC_AMR_SID(2),
3005 t_IuUP_RFC_AMR_NO_DATA(0)
3006 };
3007 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003008 mp_local_ipv4, mp_remote_ipv4);
3009 }
3010
Harald Welte00a067f2017-09-13 23:27:17 +02003011 control {
3012 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08003013 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02003014 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01003015 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08003016 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02003017 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08003018 execute(TC_crcx_early_bidir_mode());
3019 execute(TC_crcx_unsupp_param());
3020 execute(TC_crcx_missing_callid());
3021 execute(TC_crcx_missing_mode());
3022 execute(TC_crcx_unsupp_packet_intv());
3023 execute(TC_crcx_illegal_double_lco());
3024 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01003025 execute(TC_crcx_wildcarded());
3026 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08003027 execute(TC_mdcx_without_crcx());
3028 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02003029 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01003030 execute(TC_mdcx_wildcarded());
3031 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01003032 execute(TC_crcx_and_dlcx_ep_callid_connid());
3033 execute(TC_crcx_and_dlcx_ep_callid());
3034 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01003035 execute(TC_crcx_and_dlcx_ep_callid_inval());
3036 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01003037 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01003038
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003039 execute(TC_crcx_osmux_wildcard());
3040 execute(TC_crcx_osmux_fixed());
3041 execute(TC_crcx_osmux_fixed_twice());
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02003042 execute(TC_crcx_osmux_257());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003043 execute(TC_one_crcx_receive_only_osmux());
3044 execute(TC_one_crcx_loopback_osmux());
3045 execute(TC_two_crcx_and_rtp_osmux());
3046 execute(TC_two_crcx_and_rtp_osmux_bidir());
Pau Espin Pedrol26258472022-10-25 12:51:24 +02003047 execute(TC_two_crcx_and_rtp_osmux_bidir_amr_bwe());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003048 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
3049 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
3050
Harald Welte33d82162017-12-28 03:21:57 +01003051 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02003052
3053 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02003054
3055 execute(TC_one_crcx_receive_only_rtp());
3056 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02003057 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02003058 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02003059 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02003060 execute(TC_two_crcx_diff_pt_and_rtp());
3061 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02003062 execute(TC_two_crcx_mdcx_and_rtp());
3063 execute(TC_two_crcx_and_unsolicited_rtp());
3064 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01003065 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier8ed48c52023-02-07 11:24:31 +01003066 execute(TC_ts101318_rfc5993_rtp_conversion_fmtp());
Philipp Maier4f764ce2019-03-07 10:54:10 +01003067 execute(TC_amr_oa_bwe_rtp_conversion());
3068 execute(TC_amr_oa_oa_rtp_conversion());
3069 execute(TC_amr_bwe_bwe_rtp_conversion());
Philipp Maier71484282023-04-04 18:00:53 +02003070 execute(TC_amr_oa_oa_no_bwe_rtp_conversion());
3071 execute(TC_amr_oa_no_bwe_oa_rtp_conversion());
3072 execute(TC_amr_bwe_bwe_no_oa_rtp_conversion());
3073 execute(TC_amr_bwe_no_oa_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02003074
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01003075 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02003076
3077 execute(TC_e1_crcx_and_dlcx_ep());
3078 execute(TC_e1_crcx_with_overlap());
3079 execute(TC_e1_crcx_loopback());
Philipp Maier13aff992022-06-30 16:19:59 +02003080 execute(TC_e1_dlcx_wildcarded());
Philipp Maier2609c752020-07-08 12:38:09 +02003081
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02003082 execute(TC_crcx_mdcx_ip4());
3083 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02003084 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
3085 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
3086 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
3087 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
3088 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
3089 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
3090 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
3091 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02003092
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003093 execute(TC_two_crcx_mdcx_and_iuup());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003094 execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered());
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003095 execute(TC_two_crcx_mdcx_and_iuup_rtp());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003096 execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered());
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003097
Oliver Smith871f45a2023-01-24 16:21:50 +01003098 execute(TC_two_crcx_mdcx_and_rtp_clearmode());
3099
Philipp Maier37965082021-05-25 16:44:25 +02003100 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
3101 * older versions of osmo-mgw. This eventually leads into
3102 * a failure of all subsequent testcases, so it is important
3103 * not to add new testcaes after this one. */
3104 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02003105 }
3106}