blob: c9e355bcf7315f53b9a8c79707be59afc1dd4c2b [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
358 /* To be used with f_flow_create/modify... functions */
359 function f_gen_sdp(RtpFlowData flow) runs on dummy_CT return SDP_Message {
360 var template SDP_Message sdp;
361 var SDP_fmt_list fmt_list := {};
362 var SDP_attribute_list attributes := {};
363
364 /* Add SDP RTMAP attributes (codec type, referenced by PT) */
365 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
366 attributes := attributes & { valueof(ts_SDP_rtpmap(flow.codec_descr[i].pt,
367 flow.codec_descr[i].codec)) };
368 }
369
370 /* Add SDP PTIME attribute, regardless of which codec, the packet intervall remains the same */
371 attributes := attributes & { valueof(ts_SDP_ptime(20)) };
372
373 /* Add SDP FMTP attributes (codec parameters for each codec, referenced by PT) */
374 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
375 if (isvalue(flow.codec_descr[i].fmtp) and flow.codec_descr[i].fmtp != "") {
376 attributes := attributes & { valueof(ts_SDP_fmtp(flow.codec_descr[i].pt,
377 flow.codec_descr[i].fmtp)) };
378 }
379 }
380
381 /* Final step: Generate SDP */
382 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
383 fmt_list := fmt_list & {int2str(flow.codec_descr[i].pt)};
384 }
385 sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42", flow.em.portnr, fmt_list, attributes);
386
387 return valueof(sdp);
388 }
389
390 /* Generate a valid RTP emulation config from the payload type numbers configured in the flow description and
391 * the the default configuration of the RTP emulation module. */
392 function f_gen_rtpem_config_from_flow(RtpFlowData flow) return RtpemConfig {
393 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
394
395 for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
396 var RtpemConfigPayload tx_cfg_payload;
397 var RtpemConfigPayload rx_cfg_payload;
398
399 tx_cfg_payload.payload_type := flow.codec_descr[i].pt;
400 tx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.tx_payloads[0].fixed_payload;
401 rx_cfg_payload.payload_type := flow.codec_descr[i].pt;
402 rx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.rx_payloads[0].fixed_payload;
403
404 rtp_cfg.tx_payloads := rtp_cfg.tx_payloads & {tx_cfg_payload};
405 rtp_cfg.rx_payloads := rtp_cfg.rx_payloads & {rx_cfg_payload};
406 }
407
408 return rtp_cfg;
Harald Weltebb7523b2018-03-29 08:52:01 +0200409 }
410
Philipp Maier2321ef92018-06-27 17:52:04 +0200411 /* Create an RTP flow (bidirectional, or receive-only) */
412 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 +0200413 boolean one_phase := true)
414 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200415 var template MgcpCommand cmd;
416 var MgcpResponse resp;
417
418 /* bind local RTP emulation socket */
419 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
420
Philipp Maier28bb8292018-07-20 17:09:17 +0200421 /* configure rtp-emulation */
422 if (ispresent(flow.rtp_cfg)) {
423 f_rtpem_configure(pt, flow.rtp_cfg);
424 } else {
Philipp Maierbbe454d2023-03-28 15:31:57 +0200425 f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow));
Philipp Maier28bb8292018-07-20 17:09:17 +0200426 }
427
Harald Weltebb7523b2018-03-29 08:52:01 +0200428 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200429 /* Connect flow to MGW using a CRCX that also contains an SDP
430 * part that tells the MGW where we are listening for RTP streams
431 * that come from the MGW. We get a fully working connection in
432 * one go. */
433
434 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200435 cmd.sdp := f_gen_sdp(flow);
Philipp Maierc8c0b402019-03-07 10:48:45 +0100436
Harald Weltebb7523b2018-03-29 08:52:01 +0200437 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
438 flow.mgcp_conn_id := extract_conn_id(resp);
439 /* extract port number from response */
440 flow.mgw.portnr :=
441 resp.sdp.media_list[0].media_field.ports.port_number;
442 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200443 /* Create a half-open connection only. We do not tell the MGW
444 * where it can send RTP streams to us. This means this
445 * connection will only be able to receive but can not send
446 * data back to us. In order to turn the connection in a fully
447 * bi-directional one, a separate MDCX is needed. */
448
449 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200450 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
451 flow.mgcp_conn_id := extract_conn_id(resp);
452 /* extract MGW-side port number from response */
453 flow.mgw.portnr :=
454 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200455 }
456 /* finally, connect the emulation-side RTP socket to the MGW */
457 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
458 }
459
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200460 /* Create an Osmux flow (bidirectional, or receive-only) */
461 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
462 boolean one_phase := true)
463 runs on dummy_CT {
464 var template MgcpCommand cmd;
465 var MgcpResponse resp;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200466 var OsmuxTxHandle tx_hdl;
467 var OsmuxRxHandle rx_hdl;
468 var charstring cid_response;
469 var OsmuxCID cid_resp_parsed
470
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200471 /* bind local Osmux emulation socket */
472 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
473
474 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200475 if (ispresent(flow.osmux.cfg)) {
476 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200477 } else {
478 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
479 f_osmuxem_configure(pt, osmux_cfg);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200480 flow.osmux.cfg := osmux_cfg
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200481 }
482
483 if (one_phase) {
484 /* Connect flow to MGW using a CRCX that also contains an SDP
485 * part that tells the MGW where we are listening for Osmux streams
486 * that come from the MGW. We get a fully working connection in
487 * one go. */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200488 if (flow.osmux.local_cid != -1) {
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200489 /* We may still want to negotiate osmux CID later at MDCX */
490 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200491 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200492 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200493 flow.osmux.local_cid_sent := true;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200494 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200495 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200496 cmd.sdp := f_gen_sdp(flow);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200497 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
498 flow.mgcp_conn_id := extract_conn_id(resp);
499 /* extract port number from response */
500 flow.mgw.portnr :=
501 resp.sdp.media_list[0].media_field.ports.port_number;
502 } else {
503 /* Create a half-open connection only. We do not tell the MGW
504 * where it can send Osmux streams to us. This means this
505 * connection will only be able to receive but can not send
506 * data back to us. In order to turn the connection in a fully
507 * bi-directional one, a separate MDCX is needed. */
508
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200509 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200510 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
511
512 flow.mgcp_conn_id := extract_conn_id(resp);
513 /* extract MGW-side port number from response */
514 flow.mgw.portnr :=
515 resp.sdp.media_list[0].media_field.ports.port_number;
516 }
517
518 /* extract Osmux CID we got assigned by the MGW */
519 var MgcpMessage resp_msg := {
520 response := resp
521 }
522
523 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
524 setverdict(fail, "No Osmux CID in MGCP response", resp);
525 mtc.stop;
526 }
527
528 /* Make sure response is no wildcard */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200529 flow.osmux.remote_cid := f_mgcp_osmux_cid_decode(cid_response);
530 if (flow.osmux.remote_cid == -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200531 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
532 mtc.stop;
533 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200534 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux.remote_cid));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200535 f_osmuxem_register_txhandle(pt, tx_hdl);
536
537 /* finally, connect the emulation-side RTP socket to the MGW */
538 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
539 }
540
Philipp Maier2321ef92018-06-27 17:52:04 +0200541 /* Modify an existing RTP flow */
542 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
543 runs on dummy_CT {
544 var template MgcpCommand cmd;
545 var MgcpResponse resp;
546
547 /* rebind local RTP emulation socket to the new address */
548 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
549
Philipp Maier28bb8292018-07-20 17:09:17 +0200550 /* reconfigure rtp-emulation */
551 if (ispresent(flow.rtp_cfg)) {
552 f_rtpem_configure(pt, flow.rtp_cfg);
553 } else {
Philipp Maierbbe454d2023-03-28 15:31:57 +0200554 f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow));
Philipp Maier28bb8292018-07-20 17:09:17 +0200555 }
556
Philipp Maier2321ef92018-06-27 17:52:04 +0200557 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
558 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
Philipp Maierbbe454d2023-03-28 15:31:57 +0200559 cmd.sdp := f_gen_sdp(flow);
Philipp Maier2321ef92018-06-27 17:52:04 +0200560 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
561
562 /* extract MGW-side port number from response. (usually this
563 * will not change, but thats is up to the MGW) */
564 flow.mgw.portnr :=
565 resp.sdp.media_list[0].media_field.ports.port_number;
566
567 /* reconnect the emulation-side RTP socket to the MGW */
568 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
569 }
570
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200571 /* Modify an existing Osmux flow */
572 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
573 runs on dummy_CT {
574 var template MgcpCommand cmd;
575 var MgcpResponse resp;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200576 var OsmuxRxHandle rx_hdl;
577 var charstring cid_response;
578 var OsmuxCID cid_resp_parsed
579
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200580 /* rebind local Osmux emulation socket to the new address */
581 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
582
583 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200584 if (ispresent(flow.osmux.cfg)) {
585 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200586 } else {
587 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
588 f_osmuxem_configure(pt, osmux_cfg);
589 }
590
591 /* 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 +0200592 if (flow.osmux.local_cid_sent == false and flow.osmux.local_cid != -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200593 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200594 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200595 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200596 flow.osmux.local_cid_sent := true;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200597 }
598
599 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200600 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 +0200601 cmd.sdp := f_gen_sdp(flow);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200602 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
603
604 /* extract MGW-side port number from response. (usually this
605 * will not change, but thats is up to the MGW) */
606 flow.mgw.portnr :=
607 resp.sdp.media_list[0].media_field.ports.port_number;
608
609 /* extract Osmux CID we got assigned by the MGW */
610 var MgcpMessage resp_msg := {
611 response := resp
612 }
613
614 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
615 setverdict(fail, "No Osmux CID in MGCP response", resp);
616 mtc.stop;
617 }
618
619 /* Make sure response is no wildcard */
620 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
621 if (cid_resp_parsed == -1) {
622 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
623 mtc.stop;
624 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200625 if (cid_resp_parsed != flow.osmux.remote_cid) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200626 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
627 mtc.stop;
628 }
629
630 /* reconnect the emulation-side Osmux socket to the MGW */
631 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
632 }
633
Philipp Maier2321ef92018-06-27 17:52:04 +0200634 /* Delete an existing RTP flow */
635 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
636 runs on dummy_CT {
637 var template MgcpCommand cmd;
638 var MgcpResponse resp;
639
640 /* Switch off RTP flow */
641 f_rtpem_mode(pt, RTPEM_MODE_NONE);
642
643 /* Delete connection on MGW (if needed) */
644 if (isvalue(call_id) and isvalue(ep)) {
645 f_sleep(0.1);
646 f_dlcx_ok(valueof(ep), call_id);
647 }
648 }
649
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200650 /* Delete an existing Osmux flow */
651 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
652 runs on dummy_CT {
653 var template MgcpCommand cmd;
654 var MgcpResponse resp;
655
656 /* Switch off Osmux flow */
657 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
658
659 /* Delete connection on MGW (if needed) */
660 if (isvalue(call_id) and isvalue(ep)) {
661 f_sleep(0.1);
662 f_dlcx_ok(valueof(ep), call_id);
663 }
664 }
665
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100666 function f_crcx(charstring ep_prefix) runs on dummy_CT {
667 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800668 var template MgcpCommand cmd;
669 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100670 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800671
Harald Welteedc45c12017-11-18 19:15:05 +0100672 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800673
Harald Welteba62c8c2017-11-18 18:26:49 +0100674 /* create the connection on the MGW */
675 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100676 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100677 extract_conn_id(resp);
678
679 /* clean-up */
680 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100681 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100682
Philipp Maier45635f42018-06-05 17:28:02 +0200683 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
684 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
685 var template MgcpCommand cmd;
686 var MgcpResponse resp;
687 var MgcpCallId call_id := '1234'H;
688
689 f_init(ep);
690
691 /* create the connection on the MGW */
692 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
693 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
694 extract_conn_id(resp);
695
696 /* clean-up */
697 f_dlcx_ok(ep, call_id);
698
699 /* See also OS#2658: Even when we omit the LCO information, we
700 expect the MGW to pick a sane payload type for us. This
701 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200702 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200703 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200704 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200705 }
706
707 /* See also OS#2658: We also expect the MGW to assign a port
708 number to us. */
709 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
710 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200711 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200712 }
713 }
714
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200715 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
716 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
717 var template MgcpCommand cmd;
718 var MgcpResponse resp;
719 var MgcpCallId call_id := '1234'H;
720 var charstring cid_response;
721
722 if (run_init) {
723 f_init(ep, true);
724 }
725
726 /* create the connection on the MGW */
727 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
728 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
729 extract_conn_id(resp);
730
731 /* extract Osmux CID we got assigned by the MGW */
732 var MgcpMessage resp_msg := {
733 response := resp
734 }
735
736 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
737 setverdict(fail, "No Osmux CID in MGCP response", resp);
738 mtc.stop;
739 }
740
741 /* Make sure response is no wildcard */
742 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
743 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
744 mtc.stop;
745 }
746
747 /* clean-up */
748 f_dlcx_ok(ep, call_id);
749 }
750
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100751 /* test valid CRCX without SDP */
752 testcase TC_crcx() runs on dummy_CT {
753 f_crcx(c_mgw_ep_rtpbridge);
754 setverdict(pass);
755 }
756
Philipp Maier45635f42018-06-05 17:28:02 +0200757 /* test valid CRCX without SDP and LCO */
758 testcase TC_crcx_no_lco() runs on dummy_CT {
759 f_crcx_no_lco(c_mgw_ep_rtpbridge);
760 setverdict(pass);
761 }
762
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100763 /* test valid CRCX without SDP (older method without endpoint prefix) */
764 testcase TC_crcx_noprefix() runs on dummy_CT {
765 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800766 setverdict(pass);
767 }
768
769 /* test CRCX with unsupported mode, expect 517 */
770 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
771 var template MgcpCommand cmd;
772 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100773 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100774 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800775 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
776
Harald Welteedc45c12017-11-18 19:15:05 +0100777 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800778
Harald Welteba62c8c2017-11-18 18:26:49 +0100779 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800780 resp := mgcp_transceive_mgw(cmd, rtmpl);
781 setverdict(pass);
782 }
783
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200784 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
785 testcase TC_crcx_osmo_ign() runs on dummy_CT {
786 var template MgcpCommand cmd;
787 var MgcpResponse resp;
788 var MgcpEndpoint ep := "7@" & c_mgw_domain;
789 var MgcpCallId call_id := '3'H;
790
791 f_init(ep);
792
793 /* CRCX 1 7@mgw MGCP 1.0
794 C: 3
795 L: p:20, a:GSM-EFR, nt:IN
796 M: recvonly
797 X-Osmo-IGN: C
798 */
799
800 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
801 cmd.params := {ts_MgcpParCallId(call_id),
802 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
803 t_MgcpParConnMode("recvonly"),
804 t_MgcpParOsmoIGN("C")};
805 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
806 extract_conn_id(resp);
807
808 /* clean-up */
809 f_dlcx_ok(ep, call_id);
810 setverdict(pass);
811 }
812
Harald Welte21ba5572017-09-19 17:55:05 +0800813 /* test CRCX with early bi-directional mode, expect 527 as
814 * bi-diretional media can only be established once both local and
815 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800816 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
817 var template MgcpCommand cmd;
818 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100819 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100820 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800821 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
822
Harald Welteedc45c12017-11-18 19:15:05 +0100823 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800824
Harald Welteba62c8c2017-11-18 18:26:49 +0100825 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800826 resp := mgcp_transceive_mgw(cmd, rtmpl);
827 setverdict(pass);
828 }
829
830 /* test CRCX with unsupported Parameters */
831 testcase TC_crcx_unsupp_param() runs on dummy_CT {
832 var template MgcpCommand cmd;
833 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100834 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100835 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800836 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
837
Harald Welteedc45c12017-11-18 19:15:05 +0100838 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800839
Harald Welteba62c8c2017-11-18 18:26:49 +0100840 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100841 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
842 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
843
Harald Weltee636afd2017-09-17 16:24:09 +0800844 resp := mgcp_transceive_mgw(cmd, rtmpl);
845 setverdict(pass);
846 }
847
848 /* test CRCX with missing CallId */
849 testcase TC_crcx_missing_callid() runs on dummy_CT {
850 var template MgcpCommand cmd;
851 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100852 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100853 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800854
Harald Welteedc45c12017-11-18 19:15:05 +0100855 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800856
Harald Welteba62c8c2017-11-18 18:26:49 +0100857 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800858 cmd.params := {
859 t_MgcpParConnMode("recvonly"),
860 t_MgcpParLocConnOpt("p:20")
861 }
862 resp := mgcp_transceive_mgw(cmd, rtmpl);
863 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100864
Harald Weltee636afd2017-09-17 16:24:09 +0800865 }
866
867 /* test CRCX with missing Mode */
868 testcase TC_crcx_missing_mode() runs on dummy_CT {
869 var template MgcpCommand cmd;
870 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100871 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100872 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100873 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800874
Harald Welteedc45c12017-11-18 19:15:05 +0100875 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800876
Harald Welteba62c8c2017-11-18 18:26:49 +0100877 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800878 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100879 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800880 t_MgcpParLocConnOpt("p:20")
881 }
882 resp := mgcp_transceive_mgw(cmd, rtmpl);
883 setverdict(pass);
884 }
885
886 /* test CRCX with unsupported packetization interval */
887 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
888 var template MgcpCommand cmd;
889 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100890 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100891 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100892 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800893
Harald Welteedc45c12017-11-18 19:15:05 +0100894 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800895
Harald Welteba62c8c2017-11-18 18:26:49 +0100896 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100897 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800898 resp := mgcp_transceive_mgw(cmd, rtmpl);
899 setverdict(pass);
900 }
901
902 /* test CRCX with illegal double presence of local connection option */
903 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
904 var template MgcpCommand cmd;
905 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100906 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100907 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800908 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
909
Harald Welteedc45c12017-11-18 19:15:05 +0100910 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800911
Harald Welteba62c8c2017-11-18 18:26:49 +0100912 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100913 /* p:20 is permitted only once and not twice! */
914 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800915 resp := mgcp_transceive_mgw(cmd, rtmpl);
916 setverdict(pass);
917 }
918
919 /* test valid CRCX with valid SDP */
920 testcase TC_crcx_sdp() runs on dummy_CT {
921 var template MgcpCommand cmd;
922 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100923 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100924 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800925
Harald Welteedc45c12017-11-18 19:15:05 +0100926 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800927
Harald Welteba62c8c2017-11-18 18:26:49 +0100928 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800929 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
930 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
931 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100932 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100933
934 /* clean-up */
935 f_dlcx_ok(ep, call_id);
936
Harald Weltee636afd2017-09-17 16:24:09 +0800937 setverdict(pass);
938 }
939
Philipp Maier5e06cee2018-02-01 18:28:08 +0100940 /* test valid wildcarded CRCX */
941 testcase TC_crcx_wildcarded() runs on dummy_CT {
942 var template MgcpCommand cmd;
943 var MgcpResponse resp;
944 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
945 var MgcpCallId call_id := '1234'H;
946 var MgcpEndpoint ep_assigned;
947 f_init();
948
949 /* create the connection on the MGW */
950 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
951 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
952 extract_conn_id(resp);
953
954 /* extract endpoint name we got assigned by the MGW */
955 var MgcpMessage resp_msg := {
956 response := resp
957 }
958 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
959 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200960 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100961 }
962
963 /* clean-up */
964 f_dlcx_ok(ep_assigned, call_id);
965
966 setverdict(pass);
967 }
968
969 /* test valid wildcarded CRCX */
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200970 type record of MgcpEndpoint MgcpEndpointList;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100971 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maier5e06cee2018-02-01 18:28:08 +0100972 var integer i;
973 var template MgcpCommand cmd;
974 var MgcpResponse resp;
975 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
976 var MgcpCallId call_id := '1234'H;
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200977 var MgcpEndpoint ep_assigned;
978 var MgcpEndpointList ep_assigned_li := {};
Philipp Maier5e06cee2018-02-01 18:28:08 +0100979 f_init();
980
981 /* Exhaust all endpoint resources on the virtual trunk */
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200982 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier5e06cee2018-02-01 18:28:08 +0100983 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
984 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
985
986 /* Make sure we got a connection id */
987 extract_conn_id(resp);
988
989 var MgcpMessage resp_msg := {
990 response := resp
991 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200992 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
Philipp Maier5e06cee2018-02-01 18:28:08 +0100993 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200994 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100995 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200996 ep_assigned_li := ep_assigned_li & {ep_assigned}
Philipp Maier5e06cee2018-02-01 18:28:08 +0100997 }
998
999 /* Try to allocate one more endpoint, which should fail */
1000 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1001 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
1002 resp := mgcp_transceive_mgw(cmd, rtmpl);
1003 setverdict(pass);
1004
1005 /* clean-up */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001006 for (i := 0; i < mp_num_endpoints; i := i+1) {
1007 f_dlcx_ok(ep_assigned_li[i], call_id);
Philipp Maier5e06cee2018-02-01 18:28:08 +01001008 }
1009 setverdict(pass);
1010 }
1011
Harald Weltee636afd2017-09-17 16:24:09 +08001012 /* TODO: various SDP related bits */
1013
1014
1015 /* TODO: CRCX with X-Osmux */
1016 /* TODO: double CRCX without force_realloc */
1017
1018 /* TODO: MDCX (various) */
1019
1020 /* TODO: MDCX without CRCX first */
1021 testcase TC_mdcx_without_crcx() runs on dummy_CT {
1022 var template MgcpCommand cmd;
1023 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001024 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +01001025 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +08001026 var template MgcpResponse rtmpl := {
1027 line := {
1028 /* TODO: accept/enforce better error? */
1029 code := "400",
1030 string := ?
1031 },
1032 params:= { },
1033 sdp := omit
1034 };
1035
Harald Welteedc45c12017-11-18 19:15:05 +01001036 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001037
Harald Welte2bcfd3a2017-11-18 22:14:35 +01001038 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001039 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1040 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1041 valueof(ts_SDP_ptime(20)) });
1042 resp := mgcp_transceive_mgw(cmd, rtmpl);
1043 setverdict(pass);
1044 }
1045
1046 /* DLCX without CRCX first */
1047 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1048 var template MgcpCommand cmd;
1049 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001050 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001051 var template MgcpResponse rtmpl := {
1052 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001053 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001054 string := ?
1055 },
1056 params:= { },
1057 sdp := omit
1058 };
1059
Harald Welteedc45c12017-11-18 19:15:05 +01001060 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001061
Harald Welteedc45c12017-11-18 19:15:05 +01001062 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001063 resp := mgcp_transceive_mgw(cmd, rtmpl);
1064 setverdict(pass);
1065 }
1066
Philipp Maier21c1cff2021-07-20 14:22:53 +02001067 /* DLCX to non existing endpoint */
1068 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1069 var template MgcpCommand cmd;
1070 var MgcpResponse resp;
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001071 var charstring non_existant_ep := hex2str(int2hex(mp_num_endpoints + 1, 4))
1072 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & non_existant_ep & "@" & c_mgw_domain;
Philipp Maier21c1cff2021-07-20 14:22:53 +02001073 var template MgcpResponse rtmpl := {
1074 line := {
1075 code := ("500"),
1076 string := ?
1077 },
1078 params:= { },
1079 sdp := omit
1080 };
1081
1082 f_init(ep);
1083
1084 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1085 resp := mgcp_transceive_mgw(cmd, rtmpl);
1086 setverdict(pass);
1087 }
1088
Philipp Maier8a3dc922018-02-02 14:55:12 +01001089 /* test valid wildcarded MDCX */
1090 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1091 /* Note: A wildcarded MDCX is not allowed, so we expect the
1092 * MGW to reject this request */
1093 var template MgcpCommand cmd;
1094 var MgcpResponse resp;
1095 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1096 var MgcpCallId call_id := '1225'H;
1097 var template MgcpResponse rtmpl := {
1098 line := {
1099 /* TODO: accept/enforce better error? */
1100 code := "507",
1101 string := ?
1102 },
1103 params:= { },
1104 sdp := omit
1105 };
1106
1107 f_init(ep);
1108
1109 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1110 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1111 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1112 valueof(ts_SDP_ptime(20)) });
1113 resp := mgcp_transceive_mgw(cmd, rtmpl);
1114 setverdict(pass);
1115 }
1116
1117 /* test valid wildcarded DLCX */
1118 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001119 var template MgcpCommand cmd;
1120 var MgcpResponse resp;
1121 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001122 var integer i;
1123 var MgcpCallId call_id := '1234'H;
1124 var StatsDExpects expect;
1125 f_init(ep);
1126
1127 /* Allocate a few endpoints */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001128 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier55b90542021-07-02 12:33:19 +02001129 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1130 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1131 }
1132
Philipp Maier1298b092021-11-18 18:33:39 +01001133 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
1134 * occupied endpoints */
1135 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001136 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001137 { 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 +02001138 };
1139 f_statsd_expect(expect);
1140
1141 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001142 var template MgcpResponse rtmpl := {
1143 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001144 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001145 string := ?
1146 },
1147 params:= { },
1148 sdp := omit
1149 };
Philipp Maier55b90542021-07-02 12:33:19 +02001150 cmd := ts_DLCX(get_next_trans_id(), ep);
1151 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001152
Philipp Maier6c740e82022-06-30 12:04:34 +02001153 /* Query a the statsd once to ensure that intermediate results are pulled from the
1154 * pipeline. The second query (below) will return the actual result. */
1155 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001156 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := mp_num_endpoints}
Philipp Maier6c740e82022-06-30 12:04:34 +02001157 };
1158 f_statsd_expect(expect);
1159
1160 /* The second query must resturn a result with 0 endpoints in use. */
Philipp Maier55b90542021-07-02 12:33:19 +02001161 expect := {
1162 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1163 };
1164 f_statsd_expect(expect);
1165
Philipp Maier8a3dc922018-02-02 14:55:12 +01001166 setverdict(pass);
1167 }
1168
Harald Welte79181ff2017-11-18 19:26:11 +01001169 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1170 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1171 var template MgcpCommand cmd;
1172 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001173 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001174 var MgcpCallId call_id := '51234'H;
1175
1176 f_init(ep);
1177
1178 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1179 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1180
1181 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1182
1183 setverdict(pass);
1184 }
1185
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001186 /* test valid CRCX without SDP */
1187 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1188 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1189 setverdict(pass);
1190 }
1191
1192 /* test valid CRCX without SDP */
1193 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1194 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1195 setverdict(pass);
1196 }
1197
1198 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1199 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1200 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1201 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1202 setverdict(pass);
1203 }
1204
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02001205 /* test Creating 257 concurrent osmux conns. It should fail since maximum is 256. */
1206 testcase TC_crcx_osmux_257() runs on dummy_CT {
1207 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1208 var template MgcpCommand cmd;
1209 var MgcpResponse resp;
1210 var charstring cid_response;
1211 var integer i;
1212
1213 f_init(ep, true);
1214
1215 for (i := 0; i < 256; i := i + 1) {
1216
1217 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1218 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
1219 extract_conn_id(resp);
1220
1221 /* extract Osmux CID we got assigned by the MGW */
1222 var MgcpMessage resp_msg := {
1223 response := resp
1224 }
1225
1226 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
1227 setverdict(fail, "No Osmux CID in MGCP response", resp);
1228 mtc.stop;
1229 }
1230
1231 /* Make sure response is no wildcard */
1232 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
1233 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
1234 mtc.stop;
1235 }
1236 }
1237
1238 /* Now conn num 257, it should fail due to all Osmux conns already allocated: */
1239 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1240 resp := mgcp_transceive_mgw(cmd, tr_MgcpResp_Err("400"));
1241
1242 setverdict(pass);
1243
1244 /* Clean up */
1245 for (i := 0; i < 256; i := i + 1) {
1246 f_dlcx_ok(ep, int2hex(i, 4));
1247 }
1248 }
1249
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001250 /* Create one half open connection in receive-only mode. The MGW must accept
1251 * the packets but must not send any. */
1252 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1253 var RtpFlowData flow;
1254 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1255 var MgcpCallId call_id := '1225'H;
1256 var OsmuxemStats stats;
1257 var OsmuxTxHandle tx_hdl;
1258
1259 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001260 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001261 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001262 flow.osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001263 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1264
1265 /* create a transmitter not yet known by MGW */
1266 tx_hdl := valueof(t_TxHandleAMR590(2));
1267 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1268
1269 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1270 f_sleep(1.0);
1271 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1272
1273 stats := f_osmuxem_stats_get(OsmuxEM);
1274
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001275 if (stats.num_pkts_tx < 40 / flow.osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001276 setverdict(fail);
1277 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001278 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 +02001279 setverdict(fail);
1280 }
1281
1282 f_osmuxem_stats_err_check(stats);
1283
1284 setverdict(pass);
1285 }
1286
1287 /* Create one connection in loopback mode, test if the Osmux packets are
1288 * actually reflected */
1289 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1290 var RtpFlowData flow;
1291 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1292 var MgcpCallId call_id := '1225'H;
1293 var OsmuxemStats stats;
1294 var OsmuxTxHandle tx_hdl;
1295
1296 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001297 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001298 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001299 flow.osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001300 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1301
1302 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1303 f_sleep(1.0);
1304
1305 /* Switch off both Tx, wait to receive delayed frames from MGW */
1306 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1307 f_sleep(0.1);
1308 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1309
1310 stats := f_osmuxem_stats_get(OsmuxEM);
1311
1312 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1313 setverdict(fail);
1314 }
1315 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1316 setverdict(fail);
1317 }
1318
1319 f_osmuxem_stats_err_check(stats);
1320
1321 setverdict(pass);
1322 }
1323
1324 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1325 * must match the reception statistics on the other side and vice versa. The
1326 * user may also supply a tolerance value (number of packets) when deviations
1327 * are acceptable */
1328 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1329 var integer plen;
1330
1331 log("stats A: ", a);
1332 log("stats B: ", b);
1333 log("tolerance: ", tolerance, " packets");
1334 log("batch_size: ", batch_size, " packets");
1335
1336 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1337
1338 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1339 return false;
1340 }
1341
1342 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1343 return false;
1344 }
1345
1346 if(a.num_pkts_tx > 0) {
1347 plen := a.bytes_payload_tx / a.num_pkts_tx;
1348 } else {
1349 plen := 0;
1350 }
1351
1352 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1353 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1354 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1355 return false;
1356 }
1357
1358 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) {
1359 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1360 return false;
1361 }
1362
1363 return true;
1364 }
1365
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001366 function f_TC_two_crcx_and_rtp_osmux(boolean bidir, boolean rtp_amr_oa,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001367 charstring local_ip_rtp, charstring remote_ip_rtp,
1368 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001369 var RtpFlowData flow[2];
1370 var RtpemStats stats_rtp;
1371 var OsmuxemStats stats_osmux;
1372 var MgcpResponse resp;
1373 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1374 var MgcpCallId call_id := '1226'H;
1375 var integer tolerance := 0;
1376
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001377 var octetstring amr_payload;
1378 var charstring fmtp;
1379
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001380 f_init(ep, true);
1381
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001382 var AMRFT cmr := AMR_FT_0;
1383 var AMRFT ft := AMR_FT_2;
1384 if (rtp_amr_oa) {
1385 fmtp := "octet-align=1";
1386 var RTP_AMR_Hdr amr_oa_hdr := valueof(ts_RTP_AMR_Hdr(enum2int(cmr), enum2int(ft)));
1387 amr_payload := enc_RTP_AMR_Hdr(amr_oa_hdr) &
1388 f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload);
1389 } else {
1390 fmtp := "octet-align=0";
1391 /* Convert OA to BWE: */
1392 var RTP_AMR_BWE_Hdr amr_bwe_hdr := valueof(ts_RTP_AMR_BWE_Hdr(enum2int(cmr), enum2int(ft)));
1393 var bitstring amr_bwe_hdr_bits := substr(oct2bit(enc_RTP_AMR_BWE_Hdr(amr_bwe_hdr)), 0 , 10);
1394 var bitstring amr_data_bits := oct2bit(f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload));
1395 var bitstring amr_payload_bits := amr_bwe_hdr_bits & substr(amr_data_bits, 0, f_amrft_payload_bits_len(enum2int(ft)));
1396 amr_payload := bit2oct(f_pad_bit(amr_payload_bits, (lengthof(amr_payload_bits)+7)/8*8, '0'B));
1397 };
1398
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001399 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001400 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001401 flow[0].rtp_cfg := c_RtpemDefaultCfg
Philipp Maierbbe454d2023-03-28 15:31:57 +02001402 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1403 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := amr_payload;
1404 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1405 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := amr_payload;
1406 flow[0].codec_descr[0].fmtp := fmtp;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001407 /* bind local RTP emulation sockets */
1408 flow[0].em.portnr := 10000;
1409 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1410
1411 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001412 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001413 flow[1].em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001414 flow[1].osmux.local_cid := 2;
1415 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001416 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1417
1418 if (bidir) {
1419 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1420 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1421
1422 /* Note: When we test bidirectional we may
1423 * loose packets during switch off because
1424 * both ends are transmitting and we only
1425 * can switch them off one by one. */
1426 tolerance := 3;
1427 } else {
1428 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1429 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1430 }
1431
1432 f_sleep(1.0);
1433
1434 /* Switch off both Tx, wait to receive delayed frames from MGW */
1435 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1436 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1437 f_sleep(0.1);
1438
1439 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1440 f_flow_delete(RTPEM[1]);
1441
1442 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1443 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001444 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 +02001445 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1446 mtc.stop;
1447 }
1448
1449 f_rtpem_stats_err_check(stats_rtp);
1450 f_osmuxem_stats_err_check(stats_osmux);
1451
1452 setverdict(pass);
1453 }
1454
1455 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1456 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001457 f_TC_two_crcx_and_rtp_osmux(false, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001458 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001459 }
1460
1461 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1462 * exchange some data in both directions */
1463 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001464 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001465 mp_local_ipv4, mp_remote_ipv4);
1466 }
1467
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001468 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1469 * exchange some data in both directions. RTP side is configured to
1470 * rx/rx AMR in bandwidth-efficient mode. */
1471 testcase TC_two_crcx_and_rtp_osmux_bidir_amr_bwe() runs on dummy_CT {
1472 f_TC_two_crcx_and_rtp_osmux(true, false, mp_local_ipv4, mp_remote_ipv4,
1473 mp_local_ipv4, mp_remote_ipv4);
1474 }
1475
1476
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001477 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1478 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001479 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001480 mp_local_ipv6, mp_remote_ipv6);
1481 }
1482 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1483 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001484 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001485 mp_local_ipv6, mp_remote_ipv6);
1486 }
1487 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1488 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
Pau Espin Pedrol26258472022-10-25 12:51:24 +02001489 f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6,
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001490 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001491 }
1492
1493
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001494 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1495 charstring local_ip_rtp, charstring remote_ip_rtp,
1496 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001497 var RtpFlowData flow[2];
1498 var RtpemStats stats_rtp;
1499 var OsmuxemStats stats_osmux;
1500 var MgcpResponse resp;
1501 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1502 var MgcpCallId call_id := '1227'H;
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001503 var integer num_pkts_tx[2], num_pkts_rx[2];
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001504 var integer temp;
1505
1506 f_init(ep, true);
1507
1508 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001509 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001510 flow[0].rtp_cfg := c_RtpemDefaultCfg
Philipp Maierbbe454d2023-03-28 15:31:57 +02001511 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
1512 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001513 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
Philipp Maierbbe454d2023-03-28 15:31:57 +02001514 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);
1515 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 +02001516 /* bind local RTP emulation sockets */
1517 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001518 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001519
1520
1521 /* Create the second connection. This connection will be also
1522 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001523 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001524 flow[1].em.portnr := mp_local_osmux_port;
1525 if (crcx_osmux_wildcard) {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001526 flow[1].osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001527 } else {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001528 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001529 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001530 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001531 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001532
1533
1534 /* The first leg starts transmitting */
1535 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1536 f_sleep(0.5);
1537 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1538 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1539 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1540 mtc.stop;
1541 }
1542 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1543 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1544 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1545 mtc.stop;
1546 }
1547
1548 /* The second leg starts transmitting a little later */
1549 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1550 f_sleep(1.0);
1551 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1552 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1553 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1554 mtc.stop;
1555 }
1556 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1557 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1558 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1559 mtc.stop;
1560 }
1561
1562 /* The first leg will now be switched into bidirectional
1563 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001564 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001565 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001566 /* At this point in time, flow[1](Osmux)->MGW->flow[0](RTP) is active,
1567 * hence if local CID was provided during CRCX we should already be seeing packets
1568 * flowing in one direction, aka stats_rtp.num_pkts_rx sould be >0 after a while: */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001569 f_sleep(0.5);
1570 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1571 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1572 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1573 mtc.stop;
1574 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001575 if (not crcx_osmux_wildcard and stats_rtp.num_pkts_rx == 0) {
1576 setverdict(fail, "received 0 packets Osmux->MGW->RTP");
1577 mtc.stop;
1578 }
1579
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001580 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1581 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1582 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1583 mtc.stop;
1584 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001585 if (stats_osmux.num_pkts_rx > 0) {
1586 setverdict(fail, "received unexpected ", stats_osmux.num_pkts_rx, " packets RTP->MGW->Osmux");
1587 mtc.stop;
1588 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001589
1590 /* When the second leg is switched into bidirectional mode
1591 * as well, then the MGW will connect the two together and
1592 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001593 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001594 if (crcx_osmux_wildcard) {
Pau Espin Pedrol982b9792022-10-07 11:48:13 +02001595 /* We set now the local CID in MDCX: */
1596 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001597 }
1598 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001599 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1600 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1601 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
1602 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
1603 num_pkts_rx[0] := stats_rtp.num_pkts_rx;
1604 num_pkts_rx[1] := stats_osmux.num_pkts_rx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001605 f_sleep(2.0);
1606
1607 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1608 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1609
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001610 temp := (stats_rtp.num_pkts_tx - num_pkts_tx[0]) -
1611 (stats_osmux.num_pkts_rx - num_pkts_rx[1]) * flow[1].osmux.cfg.batch_size;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001612 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 +02001613 log("stats_rtp: ", stats_rtp);
1614 log("stats_osmux: ", stats_osmux);
1615 log("old_rtp_tx: ", num_pkts_tx[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001616 setverdict(fail, "RTP-Tx vs OSmux-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001617 mtc.stop;
1618 }
1619
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001620 temp := (stats_osmux.num_pkts_tx - num_pkts_tx[1]) -
1621 ((stats_rtp.num_pkts_rx - num_pkts_rx[0])/ flow[1].osmux.cfg.batch_size);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001622 if (temp > 3 or temp < -3) {
Pau Espin Pedroldb1e0682022-10-06 19:40:29 +02001623 log("stats_rtp: ", stats_rtp);
1624 log("stats_osmux: ", stats_osmux);
1625 log("old_osmux_tx: ", num_pkts_tx[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001626 setverdict(fail, "Osmux-Tx vs RTP-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001627 mtc.stop;
1628 }
1629
1630 f_rtpem_stats_err_check(stats_rtp);
1631 f_osmuxem_stats_err_check(stats_osmux);
1632
1633 /* Tear down */
1634 f_flow_delete(RTPEM[0]);
1635 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1636 setverdict(pass);
1637 }
1638
1639 /* create one RTP and one OSmux emulations and pass data in both
1640 directions. Create CRCX with wildcard Osmux CID and set it later
1641 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1642 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001643 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1644 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001645 }
1646
1647 /* create one RTP and one OSmux emulations and pass data in both
1648 directions. Create CRCX with fixed Osmux CID and keep it during
1649 MDCX. This is similar to how BSC sets up the call in AoIP. */
1650 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001651 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1652 mp_local_ipv4, mp_remote_ipv4);
1653 }
1654
1655 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1656 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1657 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1658 mp_local_ipv6, mp_remote_ipv6);
1659 }
1660 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1661 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1662 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1663 mp_local_ipv6, mp_remote_ipv6);
1664 }
1665 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1666 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1667 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1668 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001669 }
1670
Harald Welte646ecdb2017-12-28 03:21:57 +01001671 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1672 var template MgcpCommand cmd;
1673 var MgcpResponse resp;
1674
1675 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1676 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1677
1678 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1679
1680 setverdict(pass);
1681 }
1682
1683 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1684 var MgcpEndpoint ep;
1685 var MgcpCallId call_id;
1686 var integer ep_nr;
1687
1688 f_init();
1689
1690 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001691 if(ep_nr > 15) {
1692 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1693 } else {
1694 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1695 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001696 call_id := int2hex(ep_nr, 2) & '1234'H;
1697 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1698 }
1699 }
1700
Harald Welte79181ff2017-11-18 19:26:11 +01001701 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1702 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001703 var template MgcpCommand cmd;
1704 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001705 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001706 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001707
Harald Welteedc45c12017-11-18 19:15:05 +01001708 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001709
Harald Welteba62c8c2017-11-18 18:26:49 +01001710 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001711 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001712
Harald Welteba62c8c2017-11-18 18:26:49 +01001713 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001714
1715 setverdict(pass);
1716 }
1717
Harald Welte79181ff2017-11-18 19:26:11 +01001718 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1719 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1720 var template MgcpCommand cmd;
1721 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001722 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001723 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001724
1725 f_init(ep);
1726
1727 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1728 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1729
1730 f_dlcx_ok(ep);
1731
1732 setverdict(pass);
1733 }
1734
1735
Harald Welte6d167f82017-11-18 19:41:35 +01001736 /* CRCX + DLCX of valid endpoint but invalid call-id */
1737 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1738 var template MgcpCommand cmd;
1739 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001740 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001741 var MgcpCallId call_id := '51231'H;
1742
1743 f_init(ep);
1744
1745 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1746 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1747
1748 f_dlcx(ep, "516", *, 'ffff'H);
1749
1750 setverdict(pass);
1751 }
1752
1753
1754 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1755 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1756 var template MgcpCommand cmd;
1757 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001758 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001759 var MgcpCallId call_id := '51230'H;
1760
1761 f_init(ep);
1762
1763 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1764 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1765
1766 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1767
1768 setverdict(pass);
1769 }
1770
1771
Harald Weltee636afd2017-09-17 16:24:09 +08001772 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001773 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1774 var template MgcpCommand cmd;
1775 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001776 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001777 var MgcpCallId call_id := '51229'H;
1778 var template MgcpResponse rtmpl := {
1779 line := {
1780 code := "200",
1781 string := "OK"
1782 },
1783 params:= { },
1784 sdp := omit
1785 };
1786
1787 f_init(ep);
1788
1789 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1790 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1791
1792 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1793 resp := mgcp_transceive_mgw(cmd, rtmpl);
1794 resp := mgcp_transceive_mgw(cmd, rtmpl);
1795
1796 setverdict(pass);
1797 }
1798
Harald Weltebb7523b2018-03-29 08:52:01 +02001799 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1800 charstring codec) := {
1801 em := {
1802 hostname := host_a,
1803 portnr := omit
1804 },
1805 mgw := {
1806 hostname := host_b,
1807 portnr := omit
1808 },
Philipp Maierbbe454d2023-03-28 15:31:57 +02001809 codec_descr := {{
1810 pt := pt,
1811 codec := codec
1812 }},
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001813 osmux:= {
1814 local_cid_sent := false,
1815 local_cid := omit,
1816 remote_cid := omit,
1817 cfg := omit
1818 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001819 }
Harald Weltef53f1642017-11-18 19:57:11 +01001820
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) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002286 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
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;
2294 flow[0].codec_descr[0].fmtp := fmtp0;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002295 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2296
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002297 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002298 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002299 flow[1].em.portnr := 20000;
2300 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002301 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2302 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2303 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := pl1;
2304 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := pl1;
2305 flow[1].codec_descr[0].fmtp := fmtp1;
Philipp Maier6d4e0942019-02-21 17:35:01 +01002306 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2307
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002308 /* Send RTP packets to connection #0, receive on connection #1 */
2309 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2310 f_sleep(0.5);
2311 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002312 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002313 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2314 f_sleep(0.5);
2315 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002316
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002317 /* Send RTP packets to connection #1, receive on connection #0 */
2318 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2319 f_sleep(0.5);
2320 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2321 f_sleep(1.0);
2322 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2323 f_sleep(0.5);
2324 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2325
2326 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002327 f_flow_delete(RTPEM[0]);
2328 f_flow_delete(RTPEM[1], ep, call_id);
2329
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002330 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002331 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2332 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002333 f_rtpem_stats_err_check(stats[0]);
2334 f_rtpem_stats_err_check(stats[1]);
2335
2336 /* Turn off conversion mode */
2337 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2338
2339 setverdict(pass);
2340 }
2341
Philipp Maier8ed48c52023-02-07 11:24:31 +01002342 const octetstring rtp_hr_gsm_ts101318 := '0b11b3eede60be4e3ec68838c7b5'O;
2343 const octetstring rtp_hr_gsm_rfc5993 := '000b11b3eede60be4e3ec68838c7b5'O;
2344
2345 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2346 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "", "");
2347 }
2348
2349 testcase TC_ts101318_rfc5993_rtp_conversion_fmtp() runs on dummy_CT {
2350 f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "gsm-hr-format=ts101318", "gsm-hr-format=rfc5993");
2351 }
2352
Philipp Maier4f764ce2019-03-07 10:54:10 +01002353 /* create two local RTP emulations; create two connections on MGW EP, see if
Philipp Maiereb5e8132023-04-13 16:20:37 +02002354 * exchanged data is converted between AMR octet-aligned and bandwidth
Philipp Maier4f764ce2019-03-07 10:54:10 +01002355 * efficient-mode */
2356 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2357 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 Maiere5af8a32022-03-03 11:16:02 +01002366 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 96, "AMR/8000"));
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;
2374 flow[0].codec_descr[0].fmtp := fmtp0;
Philipp Maier4f764ce2019-03-07 10:54:10 +01002375 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2376
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002377 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002378 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002379 flow[1].em.portnr := 20000;
2380 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002381 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2382 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2383 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := pl1;
2384 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := pl1;
2385 flow[1].codec_descr[0].fmtp := fmtp1;
Philipp Maier4f764ce2019-03-07 10:54:10 +01002386 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2387
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002388 /* Send RTP packets to connection #0, receive on connection #1 */
2389 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2390 f_sleep(0.5);
2391 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002392 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002393 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2394 f_sleep(0.5);
2395 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002396
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002397 /* Send RTP packets to connection #1, receive on connection #0 */
2398 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2399 f_sleep(0.5);
2400 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2401 f_sleep(1.0);
2402 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2403 f_sleep(0.5);
2404 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2405
2406 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002407 f_flow_delete(RTPEM[0]);
2408 f_flow_delete(RTPEM[1], ep, call_id);
2409
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002410 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002411 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2412 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002413 f_rtpem_stats_err_check(stats[0]);
2414 f_rtpem_stats_err_check(stats[1]);
2415
2416 setverdict(pass);
2417 }
2418
Philipp Maier882843d2020-05-25 15:33:13 +02002419 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2420 * functions are real world AMR RTP payloads including AMR header. The
2421 * payloads were extracted from a trace with known good payloads. */
2422
Philipp Maiercd756ed2023-02-13 11:12:24 +01002423 const octetstring rtp_amr_5_90k_oa := '2014e959f35fdfe5e9667ffbc088818088'O;
2424 const octetstring rtp_amr_5_90k_bwe := '217a567cd7f7f97a599ffef022206022'O;
2425 const octetstring rtp_amr_5_15k_oa := '100c4e9ba850e30d5d53d04de41e7c'O;
2426 const octetstring rtp_amr_5_15k_bwe := '10d3a6ea1438c35754f41379079f'O;
2427
Philipp Maier4f764ce2019-03-07 10:54:10 +01002428 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maiercd756ed2023-02-13 11:12:24 +01002429 f_TC_amr_x_x_rtp_conversion(rtp_amr_5_90k_oa, rtp_amr_5_90k_bwe, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002430 }
2431
2432 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
Philipp Maiercd756ed2023-02-13 11:12:24 +01002433 f_TC_amr_x_x_rtp_conversion(rtp_amr_5_15k_oa, rtp_amr_5_15k_oa, "octet-align=1", "octet-align=1");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002434 }
2435
2436 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maiercd756ed2023-02-13 11:12:24 +01002437 f_TC_amr_x_x_rtp_conversion(rtp_amr_5_15k_bwe, rtp_amr_5_15k_bwe, "octet-align=0", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002438 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002439
Harald Weltee636afd2017-09-17 16:24:09 +08002440 /* TODO: Double-DLCX (no retransmission) */
2441
2442
2443
2444 /* TODO: AUEP (various) */
2445 /* TODO: RSIP (various) */
2446 /* TODO: RQNT (various) */
2447 /* TODO: EPCF (various) */
2448 /* TODO: AUCX (various) */
2449 /* TODO: invalid verb (various) */
2450
Oliver Smith021141e2019-06-25 12:09:01 +02002451
2452 testcase TC_conn_timeout() runs on dummy_CT {
2453 var RtpFlowData flow;
2454 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2455 var MgcpCallId call_id := '1225'H;
2456 var MGCP_RecvFrom mrf;
2457
2458 f_init(ep);
2459 log("Setting conn-timeout to 1s");
2460 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2461
2462 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002463 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002464 flow.em.portnr := 10000;
2465 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2466 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2467 f_sleep(1.5);
2468
2469 log("Stopping for 0.5s and resuming");
2470 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2471 f_sleep(0.5);
2472 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2473 f_sleep(0.1);
2474
2475 log("Stopping for 1.5s, expecting to run into timeout");
2476 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2477 f_sleep(1.5);
2478
2479 log("Resuming should fail now");
2480 f_rtpem_conn_refuse_expect(RTPEM[0]);
2481 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2482 f_sleep(0.2);
2483 f_rtpem_conn_refuse_verify(RTPEM[0]);
2484
2485 setverdict(pass);
2486 }
2487
Philipp Maier2609c752020-07-08 12:38:09 +02002488 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2489 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2490 var template MgcpCommand cmd;
2491 var MgcpResponse resp;
2492 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2493 var MgcpCallId call_id := '8376F297'H;
2494
2495 f_init(ep);
2496
2497 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2498 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2499
2500 f_dlcx_ok(ep);
2501
2502 setverdict(pass);
2503 }
2504
2505 /* Test what happens when overlapping endpoints are selected (E1) */
2506 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2507 var template MgcpCommand cmd;
2508 var MgcpResponse resp;
2509 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2510 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2511 var MgcpCallId call_id_1 := '8376F297'H;
2512 var MgcpCallId call_id_2 := '837AF2A7'H;
2513
2514 f_init();
2515
2516 /* ep_1 and ep_2 are overlapping, selecting both one after
2517 * another should work fine: */
2518 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2519 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2520 f_dlcx_ok(ep_1);
2521 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2522 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2523 f_dlcx_ok(ep_2);
2524
2525 /* When ep_1 is serving a call we can not select ep_2 becaus
2526 * it is overlapping with ep_1 */
2527 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2528 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2529 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2530 resp := mgcp_transceive_mgw(cmd, ?);
2531 if (resp.line.code != "501") {
2532 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2533 }
2534 f_dlcx_ok(ep_1);
2535
2536 setverdict(pass);
2537 }
2538
2539 /* Create one connection in loopback mode, test if the RTP packets are
2540 * actually reflected */
2541 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2542 var RtpFlowData flow;
2543 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2544 var MgcpCallId call_id := '12250989'H;
2545 var RtpemStats stats;
2546
2547 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002548 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002549 flow.em.portnr := 10000;
2550 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2551
2552 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2553 f_sleep(1.0);
2554 f_flow_delete(RTPEM[0], ep, call_id);
2555
2556 stats := f_rtpem_stats_get(RTPEM[0]);
2557
2558 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2559 setverdict(fail);
2560 }
2561 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2562 setverdict(fail);
2563 }
2564
2565 f_rtpem_stats_err_check(stats);
2566
2567 setverdict(pass);
2568 }
2569
Philipp Maier13aff992022-06-30 16:19:59 +02002570 /* test valid wildcarded DLCX on an E1 trunk */
2571 testcase TC_e1_dlcx_wildcarded() runs on dummy_CT {
2572 var template MgcpCommand cmd;
2573 var MgcpEndpoint ep;
2574 var MgcpCallId call_id := '8376F297'H;
2575 var integer n_e1_ts := 4;
2576 var StatsDExpects expect;
2577
2578 f_init();
2579
2580 /* Open a few E1 timeslots */
2581 for (var integer i := 0; i < n_e1_ts; i := i+1) {
2582 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-0@" & c_mgw_domain;
2583 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2584 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2585 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-2@" & c_mgw_domain;
2586 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2587 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2588 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-4@" & c_mgw_domain;
2589 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2590 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2591 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-6@" & c_mgw_domain;
2592 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2593 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2594 }
2595
2596 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
2597 * occupied endpoints */
2598 f_sleep(1.0)
2599 expect := {
2600 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := n_e1_ts * 4, max := n_e1_ts * 4}
2601 };
2602 f_statsd_expect(expect);
2603
2604 /* Send wildcarded DLCX */
2605 var template MgcpResponse rtmpl := {
2606 line := {
2607 code := "200",
2608 string := ?
2609 },
2610 params:= { },
2611 sdp := omit
2612 };
2613 ep := "ds/e1-1/*@" & c_mgw_domain;
2614 cmd := ts_DLCX(get_next_trans_id(), ep);
2615 mgcp_transceive_mgw(cmd, rtmpl);
2616
2617 /* Query a the statsd once to ensure that intermediate results are pulled from the
2618 * pipeline. The second query (below) will return the actual result. */
2619 expect := {
2620 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := n_e1_ts * 4}
2621 };
2622 f_statsd_expect(expect);
2623
2624 /* The second query must resturn a result with 0 endpoints in use. */
2625 expect := {
2626 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := 0}
2627 };
2628 f_statsd_expect(expect);
2629
2630 setverdict(pass);
2631 }
2632
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002633 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2634 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2635 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2636 var template MgcpCommand cmd;
2637 var MgcpResponse resp;
2638 var MgcpCallId call_id := '1234'H;
2639 var MgcpConnectionId conn_id;
2640
2641 f_init(ep);
2642
2643 /* create the connection on the MGW */
2644 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2645 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2646 conn_id := extract_conn_id(resp);
2647
2648 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2649 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2650 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2651 valueof(ts_SDP_ptime(20)) });
2652 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2653
2654 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2655 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2656 }
2657 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2658 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2659 }
2660
2661 /* clean-up */
2662 f_dlcx_ok(ep, call_id);
2663 setverdict(pass);
2664 }
2665
2666 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2667 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2668 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2669 var template MgcpCommand cmd;
2670 var MgcpResponse resp;
2671 var MgcpCallId call_id := '1234'H;
2672 var MgcpConnectionId conn_id;
2673
2674 f_init(ep);
2675
2676 /* create the connection on the MGW */
2677 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2678 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2679 conn_id := extract_conn_id(resp);
2680
2681 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2682 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2683 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2684 valueof(ts_SDP_ptime(20)) });
2685 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2686
2687 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2688 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2689 }
2690 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2691 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2692 }
2693
2694 /* clean-up */
2695 f_dlcx_ok(ep, call_id);
2696 setverdict(pass);
2697 }
2698
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002699 /* create two local RTP+IuUP emulations and pass data in both directions */
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002700 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002701 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002702 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2703 var RtpFlowData flow[2];
2704 var RtpemStats stats[2];
2705 var MgcpResponse resp;
2706 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2707 var MgcpCallId call_id := '1227'H;
2708 var integer num_pkts_tx[2];
2709 var integer temp;
2710
2711 f_init(ep);
2712
2713 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2714 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2715 flow[0].em.portnr := 10000;
2716 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002717 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2718 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002719 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002720 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002721 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002722 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2723 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2724
2725 /* Create the second connection. This connection will be also
2726 * in receive only mode (CN side, IuUP-Init passive) */
2727 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2728 flow[1].em.portnr := 20000;
2729 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002730 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2731 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002732 flow[1].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002733 flow[1].rtp_cfg.iuup_cfg.active_init := false;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002734 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2735 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2736
2737 /* The first leg starts transmitting */
2738 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2739 f_sleep(0.5);
2740 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2741 if (stats[0].num_pkts_rx_err_disabled != 0) {
2742 setverdict(fail, "received packets from MGW on recvonly connection 0");
2743 mtc.stop;
2744 }
2745 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2746 if (stats[1].num_pkts_rx_err_disabled != 0) {
2747 setverdict(fail, "received packets from MGW on recvonly connection 1");
2748 mtc.stop;
2749 }
2750
2751 /* The second leg starts transmitting a little later */
2752 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2753 f_sleep(1.0);
2754 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2755 if (stats[0].num_pkts_rx_err_disabled != 0) {
2756 setverdict(fail, "received packets from MGW on recvonly connection 0");
2757 mtc.stop;
2758 }
2759 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2760 if (stats[1].num_pkts_rx_err_disabled != 0) {
2761 setverdict(fail, "received packets from MGW on recvonly connection 1");
2762 mtc.stop;
2763 }
2764
2765 /* The first leg will now be switched into bidirectional
2766 * mode, but we do not expect any data coming back yet. */
2767 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2768 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2769 num_pkts_tx[1] := stats[1].num_pkts_tx;
2770 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2771 f_sleep(0.5);
2772 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2773 if (stats[0].num_pkts_rx_err_disabled != 0) {
2774 setverdict(fail, "received packets from MGW on recvonly connection 0");
2775 mtc.stop;
2776 }
2777 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2778 if (stats[1].num_pkts_rx_err_disabled != 0) {
2779 setverdict(fail, "received packets from MGW on recvonly connection 1");
2780 mtc.stop;
2781 }
2782
2783 /* When the second leg is switched into bidirectional mode
2784 * as well, then the MGW will connect the two together and
2785 * we should see RTP streams passing through from both ends. */
2786 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2787 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2788 num_pkts_tx[0] := stats[0].num_pkts_tx;
2789 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2790 f_sleep(2.0);
2791
2792 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2793 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2794
2795 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2796 if (temp > 3 or temp < -3) {
2797 setverdict(fail, "number of packets not within normal parameters:", temp);
2798 mtc.stop;
2799 }
2800
2801 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2802 if (temp > 3 or temp < -3) {
2803 setverdict(fail, "number of packets not within normal parameters:", temp);
2804 mtc.stop;
2805 }
2806
2807 f_rtpem_stats_err_check(stats[0]);
2808 f_rtpem_stats_err_check(stats[1]);
2809
2810 /* Tear down */
2811 f_flow_delete(RTPEM[0]);
2812 f_flow_delete(RTPEM[1], ep, call_id);
2813 setverdict(pass);
2814 }
2815 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002816 var template (value) IuUP_RabFlowCombinationList rfcl := {
2817 t_IuUP_RFC_AMR_12_2(0),
2818 t_IuUP_RFC_AMR_SID(1),
2819 t_IuUP_RFC_AMR_NO_DATA(2)
2820 };
2821 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2822 mp_local_ipv4, mp_remote_ipv4);
2823 }
2824 /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */
2825 testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT {
2826 var template (value) IuUP_RabFlowCombinationList rfcl := {
2827 t_IuUP_RFC_AMR_12_2(1),
2828 t_IuUP_RFC_AMR_SID(2),
2829 t_IuUP_RFC_AMR_NO_DATA(0)
2830 };
2831 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002832 mp_local_ipv4, mp_remote_ipv4);
2833 }
2834
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002835 /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */
2836 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 +02002837 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002838 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2839 var RtpFlowData flow[2];
2840 var RtpemStats stats[2];
2841 var MgcpResponse resp;
2842 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2843 var MgcpCallId call_id := '1227'H;
2844 var integer num_pkts_tx[2];
2845 var integer temp;
2846
2847 f_init(ep);
2848
2849 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2850 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2851 flow[0].em.portnr := 10000;
2852 flow[0].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002853 flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2854 flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
2855 flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
2856 /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
2857 flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002858 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002859 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002860 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002861 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2862 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2863
2864 /* Create the second connection. This connection will be also
2865 * in receive only mode (CN side, regular RTP) */
2866 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
2867 flow[1].em.portnr := 20000;
2868 flow[1].rtp_cfg := c_RtpemDefaultCfg;
Philipp Maierbbe454d2023-03-28 15:31:57 +02002869 flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2870 flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
2871 flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
2872 /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
2873 flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002874 flow[1].rtp_cfg.iuup_mode := false;
2875 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2876 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2877
2878 /* The first leg starts transmitting */
2879 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2880 f_sleep(0.5);
2881 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2882 if (stats[0].num_pkts_rx_err_disabled != 0) {
2883 setverdict(fail, "received packets from MGW on recvonly connection 0");
2884 mtc.stop;
2885 }
2886 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2887 if (stats[1].num_pkts_rx_err_disabled != 0) {
2888 setverdict(fail, "received packets from MGW on recvonly connection 1");
2889 mtc.stop;
2890 }
2891
2892 /* The second leg starts transmitting a little later */
2893 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2894 f_sleep(1.0);
2895 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2896 if (stats[0].num_pkts_rx_err_disabled != 0) {
2897 setverdict(fail, "received packets from MGW on recvonly connection 0");
2898 mtc.stop;
2899 }
2900 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2901 if (stats[1].num_pkts_rx_err_disabled != 0) {
2902 setverdict(fail, "received packets from MGW on recvonly connection 1");
2903 mtc.stop;
2904 }
2905
2906 /* The first leg will now be switched into bidirectional
2907 * mode, but we do not expect any data coming back yet. */
2908 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2909 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2910 num_pkts_tx[1] := stats[1].num_pkts_tx;
2911 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2912 f_sleep(0.5);
2913 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2914 if (stats[0].num_pkts_rx_err_disabled != 0) {
2915 setverdict(fail, "received packets from MGW on recvonly connection 0");
2916 mtc.stop;
2917 }
2918 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2919 if (stats[1].num_pkts_rx_err_disabled != 0) {
2920 setverdict(fail, "received packets from MGW on recvonly connection 1");
2921 mtc.stop;
2922 }
2923
2924 /* When the second leg is switched into bidirectional mode
2925 * as well, then the MGW will connect the two together and
2926 * we should see RTP streams passing through from both ends. */
2927 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2928 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2929 num_pkts_tx[0] := stats[0].num_pkts_tx;
2930 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2931 f_sleep(2.0);
2932
2933 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2934 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2935
2936 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2937 if (temp > 3 or temp < -3) {
2938 setverdict(fail, "number of packets not within normal parameters:", temp);
2939 mtc.stop;
2940 }
2941
2942 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2943 if (temp > 3 or temp < -3) {
2944 setverdict(fail, "number of packets not within normal parameters:", temp);
2945 mtc.stop;
2946 }
2947
2948 f_rtpem_stats_err_check(stats[0]);
2949 f_rtpem_stats_err_check(stats[1]);
2950
2951 /* Tear down */
2952 f_flow_delete(RTPEM[0]);
2953 f_flow_delete(RTPEM[1], ep, call_id);
2954 setverdict(pass);
2955 }
2956 testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002957 var template (value) IuUP_RabFlowCombinationList rfcl := {
2958 t_IuUP_RFC_AMR_12_2(0),
2959 t_IuUP_RFC_AMR_SID(1),
2960 t_IuUP_RFC_AMR_NO_DATA(2)
2961 };
2962 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2963 mp_local_ipv4, mp_remote_ipv4);
2964 }
2965 /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */
2966 testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT {
2967 var template (value) IuUP_RabFlowCombinationList rfcl := {
2968 t_IuUP_RFC_AMR_12_2(1),
2969 t_IuUP_RFC_AMR_SID(2),
2970 t_IuUP_RFC_AMR_NO_DATA(0)
2971 };
2972 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002973 mp_local_ipv4, mp_remote_ipv4);
2974 }
2975
Harald Welte00a067f2017-09-13 23:27:17 +02002976 control {
2977 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002978 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002979 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002980 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002981 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002982 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002983 execute(TC_crcx_early_bidir_mode());
2984 execute(TC_crcx_unsupp_param());
2985 execute(TC_crcx_missing_callid());
2986 execute(TC_crcx_missing_mode());
2987 execute(TC_crcx_unsupp_packet_intv());
2988 execute(TC_crcx_illegal_double_lco());
2989 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002990 execute(TC_crcx_wildcarded());
2991 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002992 execute(TC_mdcx_without_crcx());
2993 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02002994 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002995 execute(TC_mdcx_wildcarded());
2996 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002997 execute(TC_crcx_and_dlcx_ep_callid_connid());
2998 execute(TC_crcx_and_dlcx_ep_callid());
2999 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01003000 execute(TC_crcx_and_dlcx_ep_callid_inval());
3001 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01003002 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01003003
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003004 execute(TC_crcx_osmux_wildcard());
3005 execute(TC_crcx_osmux_fixed());
3006 execute(TC_crcx_osmux_fixed_twice());
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02003007 execute(TC_crcx_osmux_257());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003008 execute(TC_one_crcx_receive_only_osmux());
3009 execute(TC_one_crcx_loopback_osmux());
3010 execute(TC_two_crcx_and_rtp_osmux());
3011 execute(TC_two_crcx_and_rtp_osmux_bidir());
Pau Espin Pedrol26258472022-10-25 12:51:24 +02003012 execute(TC_two_crcx_and_rtp_osmux_bidir_amr_bwe());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02003013 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
3014 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
3015
Harald Welte33d82162017-12-28 03:21:57 +01003016 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02003017
3018 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02003019
3020 execute(TC_one_crcx_receive_only_rtp());
3021 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02003022 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02003023 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02003024 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02003025 execute(TC_two_crcx_diff_pt_and_rtp());
3026 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02003027 execute(TC_two_crcx_mdcx_and_rtp());
3028 execute(TC_two_crcx_and_unsolicited_rtp());
3029 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01003030 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier8ed48c52023-02-07 11:24:31 +01003031 execute(TC_ts101318_rfc5993_rtp_conversion_fmtp());
Philipp Maier4f764ce2019-03-07 10:54:10 +01003032 execute(TC_amr_oa_bwe_rtp_conversion());
3033 execute(TC_amr_oa_oa_rtp_conversion());
3034 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02003035
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01003036 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02003037
3038 execute(TC_e1_crcx_and_dlcx_ep());
3039 execute(TC_e1_crcx_with_overlap());
3040 execute(TC_e1_crcx_loopback());
Philipp Maier13aff992022-06-30 16:19:59 +02003041 execute(TC_e1_dlcx_wildcarded());
Philipp Maier2609c752020-07-08 12:38:09 +02003042
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02003043 execute(TC_crcx_mdcx_ip4());
3044 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02003045 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
3046 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
3047 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
3048 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
3049 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
3050 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
3051 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
3052 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02003053
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003054 execute(TC_two_crcx_mdcx_and_iuup());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003055 execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered());
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01003056 execute(TC_two_crcx_mdcx_and_iuup_rtp());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02003057 execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered());
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01003058
Oliver Smith871f45a2023-01-24 16:21:50 +01003059 execute(TC_two_crcx_mdcx_and_rtp_clearmode());
3060
Philipp Maier37965082021-05-25 16:44:25 +02003061 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
3062 * older versions of osmo-mgw. This eventually leads into
3063 * a failure of all subsequent testcases, so it is important
3064 * not to add new testcaes after this one. */
3065 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02003066 }
3067}