blob: 60f955f17c6bcb5375e37b14a9520b887ecc70a9 [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;
Harald Welte3c6ebb92017-09-16 00:56:57 +080027 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010028 import from General_Types all;
29 import from Native_Functions all;
30 import from IPCP_Types all;
31 import from IP_Types all;
32 import from Osmocom_VTY_Functions all;
33 import from TELNETasp_PortType all;
Philipp Maier55b90542021-07-02 12:33:19 +020034 import from StatsD_Types all;
35 import from StatsD_CodecPort all;
36 import from StatsD_CodecPort_CtrlFunct all;
37 import from StatsD_Checker all;
Philipp Maier3560bd62021-08-19 11:52:23 +020038 import from Osmocom_CTRL_Functions all;
39 import from Osmocom_CTRL_Types all;
40 import from Osmocom_CTRL_Adapter all;
Harald Welte00a067f2017-09-13 23:27:17 +020041
Philipp Maierbb7a01c2018-02-01 12:32:57 +010042 const charstring c_mgw_domain := "mgw";
43 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
44
Harald Welte21ba5572017-09-19 17:55:05 +080045 /* any variables declared in the component will be available to
46 * all functions that 'run on' the named component, similar to
47 * class members in C++ */
Philipp Maier3560bd62021-08-19 11:52:23 +020048 type component dummy_CT extends StatsD_ConnHdlr, CTRL_Adapter_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080049 port MGCP_CODEC_PT MGCP;
50 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010051 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080052 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010053
Philipp Maier2321ef92018-06-27 17:52:04 +020054 var RTP_Emulation_CT vc_RTPEM[3];
55 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010056
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020057 var OSMUX_Emulation_CT vc_OsmuxEM;
58 port OsmuxEM_CTRL_PT OsmuxEM;
59
Philipp Maier6137c322019-02-20 16:13:41 +010060 port TELNETasp_PT MGWVTY;
Philipp Maier55b90542021-07-02 12:33:19 +020061
62 var StatsD_Checker_CT vc_STATSD;
Harald Welte00a067f2017-09-13 23:27:17 +020063 };
64
Harald Weltee1e18c52017-09-17 16:23:07 +080065 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
66 var MgcpTransId tid := int2str(g_trans_id);
67 g_trans_id := g_trans_id + 1;
68 return tid;
69 }
70
Harald Welte21ba5572017-09-19 17:55:05 +080071 /* all parameters declared here can be modified / overridden by
72 * the config file in the [MODULE_PARAMETERS] section. If no
73 * config file is used or the file doesn't specify them, the
74 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080075 modulepar {
76 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020077 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020078 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080079 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020080 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020081 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010082 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020083 PortNumber mp_local_osmux_port := 1985;
Philipp Maier55b90542021-07-02 12:33:19 +020084 PortNumber mp_mgw_statsd_port := 8125;
Philipp Maier3560bd62021-08-19 11:52:23 +020085 PortNumber mp_mgw_ctrl_port := 4267;
Harald Welte3c6ebb92017-09-16 00:56:57 +080086 }
87
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020088 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
89 /* Turn on conversion mode */
90 f_vty_enter_config(MGWVTY);
91 f_vty_transceive(MGWVTY, "mgcp");
92 if (osmux_on) {
93 f_vty_transceive(MGWVTY, "osmux on");
94 } else {
95 f_vty_transceive(MGWVTY, "osmux off");
96 }
97 f_vty_transceive(MGWVTY, "exit");
98 f_vty_transceive(MGWVTY, "exit");
99
100 }
101
102 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +0100103 map(self:MGWVTY, system:MGWVTY);
104 f_vty_set_prompts(MGWVTY);
105 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200106
107 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +0100108 }
109
Harald Weltebb7523b2018-03-29 08:52:01 +0200110 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
111 runs on dummy_CT {
112 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
113 map(comp_ref:RTP, system:RTP);
114 map(comp_ref:RTCP, system:RTCP);
115 comp_ref.start(RTP_Emulation.f_main());
116 }
117
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200118 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
119 runs on dummy_CT {
120 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
121 map(comp_ref:OSMUX, system:OSMUX);
122 comp_ref.start(OSMUX_Emulation.f_main());
123 }
124
Harald Welte21ba5572017-09-19 17:55:05 +0800125 /* initialization function, called by each test case at the
126 * beginning, but 'initialized' variable ensures its body is
127 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200128 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800129 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100130 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200131
Harald Welteedc45c12017-11-18 19:15:05 +0100132 if (initialized == false) {
133 initialized := true;
134
135 /* some random number for the initial transaction id */
136 g_trans_id := float2int(rnd()*65535.0);
137 map(self:MGCP, system:MGCP_CODEC_PT);
138 /* connect the MGCP test port using the given
139 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
140 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200141 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 +0200142 if (not ispresent(res.connId)) {
143 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200144 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200145 }
Harald Welteedc45c12017-11-18 19:15:05 +0100146 g_mgcp_conn_id := res.connId;
147
Harald Weltebb7523b2018-03-29 08:52:01 +0200148 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
149 f_rtpem_init(vc_RTPEM[i], i);
150 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
151 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200152 if (osmux_on) {
153 f_osmuxem_init(vc_OsmuxEM);
154 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
155 }
Philipp Maier55b90542021-07-02 12:33:19 +0200156
Philipp Maier2ff3e662021-08-19 10:52:33 +0200157 f_init_statsd("VirtCallAgent", vc_STATSD, mp_local_ipv4, mp_mgw_statsd_port);
Philipp Maier55b90542021-07-02 12:33:19 +0200158 connect(self:STATSD_PROC, vc_STATSD:STATSD_PROC);
159
160 f_statsd_reset();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800161 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800162
Philipp Maier3560bd62021-08-19 11:52:23 +0200163 f_ipa_ctrl_start_client(mp_remote_ipv4, mp_mgw_ctrl_port);
164
Harald Welteedc45c12017-11-18 19:15:05 +0100165 if (isvalue(ep)) {
166 /* do a DLCX on all connections of the EP */
167 f_dlcx_ignore(valueof(ep));
168 }
Philipp Maier6137c322019-02-20 16:13:41 +0100169
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200170 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800171 }
172
Harald Welte00a067f2017-09-13 23:27:17 +0200173 testcase TC_selftest() runs on dummy_CT {
174 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 +0100175 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 +0200176 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
177 "I: 1\n" &
178 "\n" &
179 "v=0\r\n" &
180 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
181 "s=-\r\n" &
182 "c=IN IP4 0.0.0.0\r\n" &
183 "t=0 0\r\n" &
184 "m=audio 0 RTP/AVP 126\r\n" &
185 "a=rtpmap:126 AMR/8000\r\n" &
186 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100187 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 +0200188 "M: sendrecv\r" &
189 "C: 2\r\n" &
190 "I: 1\r\n" &
191 "L: p:20, a:AMR, nt:IN\r\n" &
192 "\n" &
193 "v=0\r\n" &
194 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800195 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200196 "c=IN IP4 0.0.0.0\r\n" &
197 "t=0 0\r\n" &
198 "m=audio 4441 RTP/AVP 99\r\n" &
199 "a=rtpmap:99 AMR/8000\r\n" &
200 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800201 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200202
203 log(c_auep);
204 log(dec_MgcpCommand(c_auep));
205
206 log(c_mdcx3);
207 log(dec_MgcpCommand(c_mdcx3));
208
209 log(c_mdcx3_ret);
210 log(dec_MgcpResponse(c_mdcx3_ret));
211
212 log(c_mdcx4);
213 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800214
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100215 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
216 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 +0800217
218 log(c_crcx510_ret);
219 log(dec_MgcpResponse(c_crcx510_ret));
220 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100221
222 /* We didn't encounter any DTE, so pass the test */
223 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800224 }
225
Harald Weltee636afd2017-09-17 16:24:09 +0800226 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100227 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800228 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
229 * - CRCX with remote session description and without
230 *
231 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100232 * x packetization != 20ms
233 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800234 * x unsupported mode (517)
235 * x bidirectional mode before RemoteConnDesc: 527
236 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100237 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800238 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
239 */
240
Harald Welte21ba5572017-09-19 17:55:05 +0800241 /* build a receive template for receiving a MGCP message. You
242 * pass the MGCP response template in, and it will generate an
243 * MGCP_RecvFrom template that can match the primitives arriving on the
244 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800245 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
246 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100247 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200248 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800249 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200250 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800251 locPort := mp_local_udp_port,
252 msg := { response := resp }
253 }
254 return mrf;
255 }
256
257 /* Send a MGCP request + receive a (matching!) response */
258 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
259 var MgcpMessage msg := { command := valueof(cmd) };
260 resp.line.trans_id := cmd.line.trans_id;
261 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800262 var MGCP_RecvFrom mrf;
263 timer T := 5.0;
264
Harald Welte55015362017-11-18 16:02:42 +0100265 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800266 T.start;
267 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800268 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200269 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
270 setverdict(fail, "Response didn't match template");
271 mtc.stop;
272 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800273 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200274 [] T.timeout {
275 setverdict(fail, "Timeout waiting for response to ", cmd);
276 mtc.stop;
277 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800278 }
279 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800280
281 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
282 return mrf.msg.response;
283 } else {
284 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
285 return r;
286 }
Harald Welte00a067f2017-09-13 23:27:17 +0200287 }
288
Harald Welteba62c8c2017-11-18 18:26:49 +0100289 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
290 var integer i;
291 for (i := 0; i < lengthof(resp.params); i := i + 1) {
292 var MgcpParameter par := resp.params[i];
293 if (par.code == "I") {
294 return str2hex(par.val);
295 }
296 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200297 setverdict(fail, "Could not find conn id for MgcpReponse");
298 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100299 return '00000000'H;
300 }
301
Harald Welte10889c12017-11-18 19:40:31 +0100302 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
303 template MgcpCallId call_id := omit,
304 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100305 var template MgcpCommand cmd;
306 var MgcpResponse resp;
307 var template MgcpResponse rtmpl := {
308 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100309 code := ret_code,
310 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100311 },
312 params := *,
313 sdp := *
314 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100315 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100316 resp := mgcp_transceive_mgw(cmd, rtmpl);
317 }
318
Harald Welte10889c12017-11-18 19:40:31 +0100319 /* Send DLCX and expect OK response */
320 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
321 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100322 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100323 }
324
Harald Welteba62c8c2017-11-18 18:26:49 +0100325 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100326 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100327 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100328 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100329 }
330
Harald Weltebb7523b2018-03-29 08:52:01 +0200331 type record HostPort {
332 charstring hostname,
333 integer portnr optional
334 }
335 type record RtpFlowData {
336 HostPort em, /* emulation side */
337 HostPort mgw, /* mgw side */
338 uint7_t pt,
339 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200340 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100341 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200342 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
343 MgcpOsmuxCID osmux_cid optional,
344 MgcpOsmuxCID osmux_cid_response optional,
345 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100346 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200347 }
348
Philipp Maier2321ef92018-06-27 17:52:04 +0200349 /* Create an RTP flow (bidirectional, or receive-only) */
350 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 +0200351 boolean one_phase := true)
352 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200353 var template MgcpCommand cmd;
354 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100355 var SDP_attribute_list attributes;
356
357 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
358 if (isvalue(flow.fmtp)) {
359 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
360 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200361
362 /* bind local RTP emulation socket */
363 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
364
Philipp Maier28bb8292018-07-20 17:09:17 +0200365 /* configure rtp-emulation */
366 if (ispresent(flow.rtp_cfg)) {
367 f_rtpem_configure(pt, flow.rtp_cfg);
368 } else {
369 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
370 rtp_cfg.tx_payload_type := flow.pt
371 f_rtpem_configure(pt, rtp_cfg);
372 }
373
Harald Weltebb7523b2018-03-29 08:52:01 +0200374 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200375 /* Connect flow to MGW using a CRCX that also contains an SDP
376 * part that tells the MGW where we are listening for RTP streams
377 * that come from the MGW. We get a fully working connection in
378 * one go. */
379
380 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200381 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100382 flow.em.portnr, { int2str(flow.pt) }, attributes);
383
Harald Weltebb7523b2018-03-29 08:52:01 +0200384 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
385 flow.mgcp_conn_id := extract_conn_id(resp);
386 /* extract port number from response */
387 flow.mgw.portnr :=
388 resp.sdp.media_list[0].media_field.ports.port_number;
389 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200390 /* Create a half-open connection only. We do not tell the MGW
391 * where it can send RTP streams to us. This means this
392 * connection will only be able to receive but can not send
393 * data back to us. In order to turn the connection in a fully
394 * bi-directional one, a separate MDCX is needed. */
395
396 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200397 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
398 flow.mgcp_conn_id := extract_conn_id(resp);
399 /* extract MGW-side port number from response */
400 flow.mgw.portnr :=
401 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200402 }
403 /* finally, connect the emulation-side RTP socket to the MGW */
404 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
405 }
406
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200407 /* Create an Osmux flow (bidirectional, or receive-only) */
408 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
409 boolean one_phase := true)
410 runs on dummy_CT {
411 var template MgcpCommand cmd;
412 var MgcpResponse resp;
413 var SDP_attribute_list attributes;
414 var OsmuxTxHandle tx_hdl;
415 var OsmuxRxHandle rx_hdl;
416 var charstring cid_response;
417 var OsmuxCID cid_resp_parsed
418
419 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
420 if (isvalue(flow.fmtp)) {
421 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
422 }
423
424 /* bind local Osmux emulation socket */
425 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
426
427 /* configure osmux-emulation */
428 if (ispresent(flow.osmux_cfg)) {
429 f_osmuxem_configure(pt, flow.osmux_cfg);
430 } else {
431 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
432 f_osmuxem_configure(pt, osmux_cfg);
433 flow.osmux_cfg := osmux_cfg
434 }
435
436 if (one_phase) {
437 /* Connect flow to MGW using a CRCX that also contains an SDP
438 * part that tells the MGW where we are listening for Osmux streams
439 * that come from the MGW. We get a fully working connection in
440 * one go. */
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200441 if (flow.osmux_cid != -1) {
442 /* We may still want to negotiate osmux CID later at MDCX */
443 rx_hdl := c_OsmuxemDefaultRxHandle;
444 rx_hdl.cid := flow.osmux_cid;
445 f_osmuxem_register_rxhandle(pt, rx_hdl);
446 flow.osmux_cid_sent := true;
447 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200448 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
449 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
450 flow.em.portnr, { int2str(flow.pt) }, attributes);
451 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
452 flow.mgcp_conn_id := extract_conn_id(resp);
453 /* extract port number from response */
454 flow.mgw.portnr :=
455 resp.sdp.media_list[0].media_field.ports.port_number;
456 } else {
457 /* Create a half-open connection only. We do not tell the MGW
458 * where it can send Osmux streams to us. This means this
459 * connection will only be able to receive but can not send
460 * data back to us. In order to turn the connection in a fully
461 * bi-directional one, a separate MDCX is needed. */
462
463 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
464 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
465
466 flow.mgcp_conn_id := extract_conn_id(resp);
467 /* extract MGW-side port number from response */
468 flow.mgw.portnr :=
469 resp.sdp.media_list[0].media_field.ports.port_number;
470 }
471
472 /* extract Osmux CID we got assigned by the MGW */
473 var MgcpMessage resp_msg := {
474 response := resp
475 }
476
477 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
478 setverdict(fail, "No Osmux CID in MGCP response", resp);
479 mtc.stop;
480 }
481
482 /* Make sure response is no wildcard */
483 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
484 if (flow.osmux_cid_response == -1) {
485 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
486 mtc.stop;
487 }
488 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
489 f_osmuxem_register_txhandle(pt, tx_hdl);
490
491 /* finally, connect the emulation-side RTP socket to the MGW */
492 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
493 }
494
Philipp Maier2321ef92018-06-27 17:52:04 +0200495 /* Modify an existing RTP flow */
496 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
497 runs on dummy_CT {
498 var template MgcpCommand cmd;
499 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100500 var SDP_attribute_list attributes;
501
502 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
503 if (isvalue(flow.fmtp)) {
504 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
505 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200506
507 /* rebind local RTP emulation socket to the new address */
508 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
509
Philipp Maier28bb8292018-07-20 17:09:17 +0200510 /* reconfigure rtp-emulation */
511 if (ispresent(flow.rtp_cfg)) {
512 f_rtpem_configure(pt, flow.rtp_cfg);
513 } else {
514 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
515 rtp_cfg.tx_payload_type := flow.pt
516 f_rtpem_configure(pt, rtp_cfg);
517 }
518
Philipp Maier2321ef92018-06-27 17:52:04 +0200519 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
520 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
521 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100522 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200523 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
524
525 /* extract MGW-side port number from response. (usually this
526 * will not change, but thats is up to the MGW) */
527 flow.mgw.portnr :=
528 resp.sdp.media_list[0].media_field.ports.port_number;
529
530 /* reconnect the emulation-side RTP socket to the MGW */
531 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
532 }
533
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200534 /* Modify an existing Osmux flow */
535 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
536 runs on dummy_CT {
537 var template MgcpCommand cmd;
538 var MgcpResponse resp;
539 var SDP_attribute_list attributes;
540 var OsmuxRxHandle rx_hdl;
541 var charstring cid_response;
542 var OsmuxCID cid_resp_parsed
543
544 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
545 if (isvalue(flow.fmtp)) {
546 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
547 }
548
549 /* rebind local Osmux emulation socket to the new address */
550 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
551
552 /* configure osmux-emulation */
553 if (ispresent(flow.osmux_cfg)) {
554 f_osmuxem_configure(pt, flow.osmux_cfg);
555 } else {
556 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
557 f_osmuxem_configure(pt, osmux_cfg);
558 }
559
560 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
561 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
562 rx_hdl := c_OsmuxemDefaultRxHandle;
563 rx_hdl.cid := flow.osmux_cid;
564 f_osmuxem_register_rxhandle(pt, rx_hdl);
565 flow.osmux_cid_sent := true;
566 }
567
568 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
569 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
570 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
571 flow.em.portnr, { int2str(flow.pt) }, attributes);
572 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
573
574 /* extract MGW-side port number from response. (usually this
575 * will not change, but thats is up to the MGW) */
576 flow.mgw.portnr :=
577 resp.sdp.media_list[0].media_field.ports.port_number;
578
579 /* extract Osmux CID we got assigned by the MGW */
580 var MgcpMessage resp_msg := {
581 response := resp
582 }
583
584 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
585 setverdict(fail, "No Osmux CID in MGCP response", resp);
586 mtc.stop;
587 }
588
589 /* Make sure response is no wildcard */
590 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
591 if (cid_resp_parsed == -1) {
592 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
593 mtc.stop;
594 }
595 if (cid_resp_parsed != flow.osmux_cid_response) {
596 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
597 mtc.stop;
598 }
599
600 /* reconnect the emulation-side Osmux socket to the MGW */
601 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
602 }
603
Philipp Maier2321ef92018-06-27 17:52:04 +0200604 /* Delete an existing RTP flow */
605 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
606 runs on dummy_CT {
607 var template MgcpCommand cmd;
608 var MgcpResponse resp;
609
610 /* Switch off RTP flow */
611 f_rtpem_mode(pt, RTPEM_MODE_NONE);
612
613 /* Delete connection on MGW (if needed) */
614 if (isvalue(call_id) and isvalue(ep)) {
615 f_sleep(0.1);
616 f_dlcx_ok(valueof(ep), call_id);
617 }
618 }
619
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200620 /* Delete an existing Osmux flow */
621 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
622 runs on dummy_CT {
623 var template MgcpCommand cmd;
624 var MgcpResponse resp;
625
626 /* Switch off Osmux flow */
627 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
628
629 /* Delete connection on MGW (if needed) */
630 if (isvalue(call_id) and isvalue(ep)) {
631 f_sleep(0.1);
632 f_dlcx_ok(valueof(ep), call_id);
633 }
634 }
635
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100636 function f_crcx(charstring ep_prefix) runs on dummy_CT {
637 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800638 var template MgcpCommand cmd;
639 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100640 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800641
Harald Welteedc45c12017-11-18 19:15:05 +0100642 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800643
Harald Welteba62c8c2017-11-18 18:26:49 +0100644 /* create the connection on the MGW */
645 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100646 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100647 extract_conn_id(resp);
648
649 /* clean-up */
650 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100651 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100652
Philipp Maier45635f42018-06-05 17:28:02 +0200653 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
654 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
655 var template MgcpCommand cmd;
656 var MgcpResponse resp;
657 var MgcpCallId call_id := '1234'H;
658
659 f_init(ep);
660
661 /* create the connection on the MGW */
662 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
663 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
664 extract_conn_id(resp);
665
666 /* clean-up */
667 f_dlcx_ok(ep, call_id);
668
669 /* See also OS#2658: Even when we omit the LCO information, we
670 expect the MGW to pick a sane payload type for us. This
671 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200672 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200673 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200674 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200675 }
676
677 /* See also OS#2658: We also expect the MGW to assign a port
678 number to us. */
679 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
680 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200681 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200682 }
683 }
684
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200685 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
686 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
687 var template MgcpCommand cmd;
688 var MgcpResponse resp;
689 var MgcpCallId call_id := '1234'H;
690 var charstring cid_response;
691
692 if (run_init) {
693 f_init(ep, true);
694 }
695
696 /* create the connection on the MGW */
697 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
698 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
699 extract_conn_id(resp);
700
701 /* extract Osmux CID we got assigned by the MGW */
702 var MgcpMessage resp_msg := {
703 response := resp
704 }
705
706 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
707 setverdict(fail, "No Osmux CID in MGCP response", resp);
708 mtc.stop;
709 }
710
711 /* Make sure response is no wildcard */
712 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
713 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
714 mtc.stop;
715 }
716
717 /* clean-up */
718 f_dlcx_ok(ep, call_id);
719 }
720
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100721 /* test valid CRCX without SDP */
722 testcase TC_crcx() runs on dummy_CT {
723 f_crcx(c_mgw_ep_rtpbridge);
724 setverdict(pass);
725 }
726
Philipp Maier45635f42018-06-05 17:28:02 +0200727 /* test valid CRCX without SDP and LCO */
728 testcase TC_crcx_no_lco() runs on dummy_CT {
729 f_crcx_no_lco(c_mgw_ep_rtpbridge);
730 setverdict(pass);
731 }
732
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100733 /* test valid CRCX without SDP (older method without endpoint prefix) */
734 testcase TC_crcx_noprefix() runs on dummy_CT {
735 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800736 setverdict(pass);
737 }
738
739 /* test CRCX with unsupported mode, expect 517 */
740 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
741 var template MgcpCommand cmd;
742 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100743 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100744 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800745 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
746
Harald Welteedc45c12017-11-18 19:15:05 +0100747 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800748
Harald Welteba62c8c2017-11-18 18:26:49 +0100749 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800750 resp := mgcp_transceive_mgw(cmd, rtmpl);
751 setverdict(pass);
752 }
753
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200754 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
755 testcase TC_crcx_osmo_ign() runs on dummy_CT {
756 var template MgcpCommand cmd;
757 var MgcpResponse resp;
758 var MgcpEndpoint ep := "7@" & c_mgw_domain;
759 var MgcpCallId call_id := '3'H;
760
761 f_init(ep);
762
763 /* CRCX 1 7@mgw MGCP 1.0
764 C: 3
765 L: p:20, a:GSM-EFR, nt:IN
766 M: recvonly
767 X-Osmo-IGN: C
768 */
769
770 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
771 cmd.params := {ts_MgcpParCallId(call_id),
772 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
773 t_MgcpParConnMode("recvonly"),
774 t_MgcpParOsmoIGN("C")};
775 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
776 extract_conn_id(resp);
777
778 /* clean-up */
779 f_dlcx_ok(ep, call_id);
780 setverdict(pass);
781 }
782
Harald Welte21ba5572017-09-19 17:55:05 +0800783 /* test CRCX with early bi-directional mode, expect 527 as
784 * bi-diretional media can only be established once both local and
785 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800786 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
787 var template MgcpCommand cmd;
788 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100789 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100790 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800791 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
792
Harald Welteedc45c12017-11-18 19:15:05 +0100793 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800794
Harald Welteba62c8c2017-11-18 18:26:49 +0100795 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800796 resp := mgcp_transceive_mgw(cmd, rtmpl);
797 setverdict(pass);
798 }
799
800 /* test CRCX with unsupported Parameters */
801 testcase TC_crcx_unsupp_param() runs on dummy_CT {
802 var template MgcpCommand cmd;
803 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100804 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100805 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800806 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
807
Harald Welteedc45c12017-11-18 19:15:05 +0100808 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800809
Harald Welteba62c8c2017-11-18 18:26:49 +0100810 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100811 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
812 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
813
Harald Weltee636afd2017-09-17 16:24:09 +0800814 resp := mgcp_transceive_mgw(cmd, rtmpl);
815 setverdict(pass);
816 }
817
818 /* test CRCX with missing CallId */
819 testcase TC_crcx_missing_callid() runs on dummy_CT {
820 var template MgcpCommand cmd;
821 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100822 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100823 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800824
Harald Welteedc45c12017-11-18 19:15:05 +0100825 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800826
Harald Welteba62c8c2017-11-18 18:26:49 +0100827 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800828 cmd.params := {
829 t_MgcpParConnMode("recvonly"),
830 t_MgcpParLocConnOpt("p:20")
831 }
832 resp := mgcp_transceive_mgw(cmd, rtmpl);
833 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100834
Harald Weltee636afd2017-09-17 16:24:09 +0800835 }
836
837 /* test CRCX with missing Mode */
838 testcase TC_crcx_missing_mode() runs on dummy_CT {
839 var template MgcpCommand cmd;
840 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100841 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100842 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100843 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800844
Harald Welteedc45c12017-11-18 19:15:05 +0100845 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800846
Harald Welteba62c8c2017-11-18 18:26:49 +0100847 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800848 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100849 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800850 t_MgcpParLocConnOpt("p:20")
851 }
852 resp := mgcp_transceive_mgw(cmd, rtmpl);
853 setverdict(pass);
854 }
855
856 /* test CRCX with unsupported packetization interval */
857 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
858 var template MgcpCommand cmd;
859 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100860 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100861 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100862 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800863
Harald Welteedc45c12017-11-18 19:15:05 +0100864 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800865
Harald Welteba62c8c2017-11-18 18:26:49 +0100866 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100867 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800868 resp := mgcp_transceive_mgw(cmd, rtmpl);
869 setverdict(pass);
870 }
871
872 /* test CRCX with illegal double presence of local connection option */
873 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
874 var template MgcpCommand cmd;
875 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100876 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100877 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800878 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
879
Harald Welteedc45c12017-11-18 19:15:05 +0100880 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800881
Harald Welteba62c8c2017-11-18 18:26:49 +0100882 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100883 /* p:20 is permitted only once and not twice! */
884 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800885 resp := mgcp_transceive_mgw(cmd, rtmpl);
886 setverdict(pass);
887 }
888
889 /* test valid CRCX with valid SDP */
890 testcase TC_crcx_sdp() runs on dummy_CT {
891 var template MgcpCommand cmd;
892 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100893 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100894 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800895
Harald Welteedc45c12017-11-18 19:15:05 +0100896 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800897
Harald Welteba62c8c2017-11-18 18:26:49 +0100898 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800899 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
900 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
901 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100902 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100903
904 /* clean-up */
905 f_dlcx_ok(ep, call_id);
906
Harald Weltee636afd2017-09-17 16:24:09 +0800907 setverdict(pass);
908 }
909
Philipp Maier5e06cee2018-02-01 18:28:08 +0100910 /* test valid wildcarded CRCX */
911 testcase TC_crcx_wildcarded() runs on dummy_CT {
912 var template MgcpCommand cmd;
913 var MgcpResponse resp;
914 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
915 var MgcpCallId call_id := '1234'H;
916 var MgcpEndpoint ep_assigned;
917 f_init();
918
919 /* create the connection on the MGW */
920 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
921 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
922 extract_conn_id(resp);
923
924 /* extract endpoint name we got assigned by the MGW */
925 var MgcpMessage resp_msg := {
926 response := resp
927 }
928 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
929 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200930 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100931 }
932
933 /* clean-up */
934 f_dlcx_ok(ep_assigned, call_id);
935
936 setverdict(pass);
937 }
938
939 /* test valid wildcarded CRCX */
940 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maierc60e8472020-07-08 12:57:13 +0200941 const integer n_endpoints := 31;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100942 var integer i;
943 var template MgcpCommand cmd;
944 var MgcpResponse resp;
945 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
946 var MgcpCallId call_id := '1234'H;
947 var MgcpEndpoint ep_assigned[n_endpoints];
948 f_init();
949
950 /* Exhaust all endpoint resources on the virtual trunk */
951 for (i := 0; i < n_endpoints; i := i+1) {
952 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
953 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
954
955 /* Make sure we got a connection id */
956 extract_conn_id(resp);
957
958 var MgcpMessage resp_msg := {
959 response := resp
960 }
961 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
962 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200963 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100964 }
965 }
966
967 /* Try to allocate one more endpoint, which should fail */
968 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
969 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
970 resp := mgcp_transceive_mgw(cmd, rtmpl);
971 setverdict(pass);
972
973 /* clean-up */
974 for (i := 0; i < n_endpoints; i := i+1) {
975 f_dlcx_ok(ep_assigned[i], call_id);
976 }
977 setverdict(pass);
978 }
979
Harald Weltee636afd2017-09-17 16:24:09 +0800980 /* TODO: various SDP related bits */
981
982
983 /* TODO: CRCX with X-Osmux */
984 /* TODO: double CRCX without force_realloc */
985
986 /* TODO: MDCX (various) */
987
988 /* TODO: MDCX without CRCX first */
989 testcase TC_mdcx_without_crcx() runs on dummy_CT {
990 var template MgcpCommand cmd;
991 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100992 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100993 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800994 var template MgcpResponse rtmpl := {
995 line := {
996 /* TODO: accept/enforce better error? */
997 code := "400",
998 string := ?
999 },
1000 params:= { },
1001 sdp := omit
1002 };
1003
Harald Welteedc45c12017-11-18 19:15:05 +01001004 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001005
Harald Welte2bcfd3a2017-11-18 22:14:35 +01001006 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001007 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1008 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1009 valueof(ts_SDP_ptime(20)) });
1010 resp := mgcp_transceive_mgw(cmd, rtmpl);
1011 setverdict(pass);
1012 }
1013
1014 /* DLCX without CRCX first */
1015 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1016 var template MgcpCommand cmd;
1017 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001018 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001019 var template MgcpResponse rtmpl := {
1020 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001021 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001022 string := ?
1023 },
1024 params:= { },
1025 sdp := omit
1026 };
1027
Harald Welteedc45c12017-11-18 19:15:05 +01001028 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001029
Harald Welteedc45c12017-11-18 19:15:05 +01001030 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001031 resp := mgcp_transceive_mgw(cmd, rtmpl);
1032 setverdict(pass);
1033 }
1034
Philipp Maier21c1cff2021-07-20 14:22:53 +02001035 /* DLCX to non existing endpoint */
1036 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1037 var template MgcpCommand cmd;
1038 var MgcpResponse resp;
1039 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "AA@" & c_mgw_domain;
1040 var template MgcpResponse rtmpl := {
1041 line := {
1042 code := ("500"),
1043 string := ?
1044 },
1045 params:= { },
1046 sdp := omit
1047 };
1048
1049 f_init(ep);
1050
1051 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1052 resp := mgcp_transceive_mgw(cmd, rtmpl);
1053 setverdict(pass);
1054 }
1055
Philipp Maier8a3dc922018-02-02 14:55:12 +01001056 /* test valid wildcarded MDCX */
1057 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1058 /* Note: A wildcarded MDCX is not allowed, so we expect the
1059 * MGW to reject this request */
1060 var template MgcpCommand cmd;
1061 var MgcpResponse resp;
1062 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1063 var MgcpCallId call_id := '1225'H;
1064 var template MgcpResponse rtmpl := {
1065 line := {
1066 /* TODO: accept/enforce better error? */
1067 code := "507",
1068 string := ?
1069 },
1070 params:= { },
1071 sdp := omit
1072 };
1073
1074 f_init(ep);
1075
1076 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1077 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1078 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1079 valueof(ts_SDP_ptime(20)) });
1080 resp := mgcp_transceive_mgw(cmd, rtmpl);
1081 setverdict(pass);
1082 }
1083
1084 /* test valid wildcarded DLCX */
1085 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001086 var template MgcpCommand cmd;
1087 var MgcpResponse resp;
1088 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001089 const integer n_endpoints := 31;
1090 var integer i;
1091 var MgcpCallId call_id := '1234'H;
1092 var StatsDExpects expect;
1093 f_init(ep);
1094
1095 /* Allocate a few endpoints */
1096 for (i := 0; i < n_endpoints; i := i+1) {
1097 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1098 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1099 }
1100
Philipp Maier1298b092021-11-18 18:33:39 +01001101 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
1102 * occupied endpoints */
1103 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001104 expect := {
1105 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := n_endpoints, max := n_endpoints}
1106 };
1107 f_statsd_expect(expect);
1108
1109 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001110 var template MgcpResponse rtmpl := {
1111 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001112 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001113 string := ?
1114 },
1115 params:= { },
1116 sdp := omit
1117 };
Philipp Maier55b90542021-07-02 12:33:19 +02001118 cmd := ts_DLCX(get_next_trans_id(), ep);
1119 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001120
Philipp Maier55b90542021-07-02 12:33:19 +02001121 /* The second interval must resturn a result with 0 endpoints in use. */
Philipp Maier1298b092021-11-18 18:33:39 +01001122 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001123 expect := {
1124 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1125 };
1126 f_statsd_expect(expect);
1127
Philipp Maier8a3dc922018-02-02 14:55:12 +01001128 setverdict(pass);
1129 }
1130
Harald Welte79181ff2017-11-18 19:26:11 +01001131 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1132 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1133 var template MgcpCommand cmd;
1134 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001135 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001136 var MgcpCallId call_id := '51234'H;
1137
1138 f_init(ep);
1139
1140 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1141 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1142
1143 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1144
1145 setverdict(pass);
1146 }
1147
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001148 /* test valid CRCX without SDP */
1149 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1150 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1151 setverdict(pass);
1152 }
1153
1154 /* test valid CRCX without SDP */
1155 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1156 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1157 setverdict(pass);
1158 }
1159
1160 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1161 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1162 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1163 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1164 setverdict(pass);
1165 }
1166
1167 /* Create one half open connection in receive-only mode. The MGW must accept
1168 * the packets but must not send any. */
1169 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1170 var RtpFlowData flow;
1171 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1172 var MgcpCallId call_id := '1225'H;
1173 var OsmuxemStats stats;
1174 var OsmuxTxHandle tx_hdl;
1175
1176 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001177 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001178 flow.em.portnr := mp_local_osmux_port;
1179 flow.osmux_cid := -1;
1180 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1181
1182 /* create a transmitter not yet known by MGW */
1183 tx_hdl := valueof(t_TxHandleAMR590(2));
1184 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1185
1186 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1187 f_sleep(1.0);
1188 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1189
1190 stats := f_osmuxem_stats_get(OsmuxEM);
1191
1192 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1193 setverdict(fail);
1194 }
1195 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1196 setverdict(fail);
1197 }
1198
1199 f_osmuxem_stats_err_check(stats);
1200
1201 setverdict(pass);
1202 }
1203
1204 /* Create one connection in loopback mode, test if the Osmux packets are
1205 * actually reflected */
1206 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1207 var RtpFlowData flow;
1208 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1209 var MgcpCallId call_id := '1225'H;
1210 var OsmuxemStats stats;
1211 var OsmuxTxHandle tx_hdl;
1212
1213 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001214 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001215 flow.em.portnr := mp_local_osmux_port;
1216 flow.osmux_cid := 2;
1217 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1218
1219 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1220 f_sleep(1.0);
1221
1222 /* Switch off both Tx, wait to receive delayed frames from MGW */
1223 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1224 f_sleep(0.1);
1225 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1226
1227 stats := f_osmuxem_stats_get(OsmuxEM);
1228
1229 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1230 setverdict(fail);
1231 }
1232 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1233 setverdict(fail);
1234 }
1235
1236 f_osmuxem_stats_err_check(stats);
1237
1238 setverdict(pass);
1239 }
1240
1241 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1242 * must match the reception statistics on the other side and vice versa. The
1243 * user may also supply a tolerance value (number of packets) when deviations
1244 * are acceptable */
1245 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1246 var integer plen;
1247
1248 log("stats A: ", a);
1249 log("stats B: ", b);
1250 log("tolerance: ", tolerance, " packets");
1251 log("batch_size: ", batch_size, " packets");
1252
1253 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1254
1255 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1256 return false;
1257 }
1258
1259 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1260 return false;
1261 }
1262
1263 if(a.num_pkts_tx > 0) {
1264 plen := a.bytes_payload_tx / a.num_pkts_tx;
1265 } else {
1266 plen := 0;
1267 }
1268
1269 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1270 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1271 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1272 return false;
1273 }
1274
1275 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) {
1276 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1277 return false;
1278 }
1279
1280 return true;
1281 }
1282
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001283 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1284 charstring local_ip_rtp, charstring remote_ip_rtp,
1285 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001286 var RtpFlowData flow[2];
1287 var RtpemStats stats_rtp;
1288 var OsmuxemStats stats_osmux;
1289 var MgcpResponse resp;
1290 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1291 var MgcpCallId call_id := '1226'H;
1292 var integer tolerance := 0;
1293
1294 f_init(ep, true);
1295
1296 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001297 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001298 flow[0].rtp_cfg := c_RtpemDefaultCfg
1299 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1300 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1301 flow[0].rtp_cfg.rx_fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload);
1302 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1303 /* bind local RTP emulation sockets */
1304 flow[0].em.portnr := 10000;
1305 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1306
1307 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001308 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001309 flow[1].em.portnr := mp_local_osmux_port;
1310 flow[1].osmux_cid := 2;
1311 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1312 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1313
1314 if (bidir) {
1315 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1316 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1317
1318 /* Note: When we test bidirectional we may
1319 * loose packets during switch off because
1320 * both ends are transmitting and we only
1321 * can switch them off one by one. */
1322 tolerance := 3;
1323 } else {
1324 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1325 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1326 }
1327
1328 f_sleep(1.0);
1329
1330 /* Switch off both Tx, wait to receive delayed frames from MGW */
1331 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1332 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1333 f_sleep(0.1);
1334
1335 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1336 f_flow_delete(RTPEM[1]);
1337
1338 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1339 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1340 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1341 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1342 mtc.stop;
1343 }
1344
1345 f_rtpem_stats_err_check(stats_rtp);
1346 f_osmuxem_stats_err_check(stats_osmux);
1347
1348 setverdict(pass);
1349 }
1350
1351 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1352 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001353 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1354 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001355 }
1356
1357 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1358 * exchange some data in both directions */
1359 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001360 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1361 mp_local_ipv4, mp_remote_ipv4);
1362 }
1363
1364 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1365 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1366 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1367 mp_local_ipv6, mp_remote_ipv6);
1368 }
1369 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1370 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1371 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1372 mp_local_ipv6, mp_remote_ipv6);
1373 }
1374 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1375 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1376 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1377 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001378 }
1379
1380
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001381 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1382 charstring local_ip_rtp, charstring remote_ip_rtp,
1383 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001384 var RtpFlowData flow[2];
1385 var RtpemStats stats_rtp;
1386 var OsmuxemStats stats_osmux;
1387 var MgcpResponse resp;
1388 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1389 var MgcpCallId call_id := '1227'H;
1390 var integer num_pkts_tx[2];
1391 var integer temp;
1392
1393 f_init(ep, true);
1394
1395 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001396 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001397 flow[0].rtp_cfg := c_RtpemDefaultCfg
1398 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1399 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1400 flow[0].rtp_cfg.rx_fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload);
1401 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1402 /* bind local RTP emulation sockets */
1403 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001404 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001405
1406
1407 /* Create the second connection. This connection will be also
1408 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001409 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001410 flow[1].em.portnr := mp_local_osmux_port;
1411 if (crcx_osmux_wildcard) {
1412 flow[1].osmux_cid := -1;
1413 } else {
1414 flow[1].osmux_cid := 2;
1415 }
1416 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001417 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001418
1419
1420 /* The first leg starts transmitting */
1421 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1422 f_sleep(0.5);
1423 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1424 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1425 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1426 mtc.stop;
1427 }
1428 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1429 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1430 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1431 mtc.stop;
1432 }
1433
1434 /* The second leg starts transmitting a little later */
1435 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1436 f_sleep(1.0);
1437 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1438 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1439 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1440 mtc.stop;
1441 }
1442 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1443 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1444 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1445 mtc.stop;
1446 }
1447
1448 /* The first leg will now be switched into bidirectional
1449 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001450 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1451 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1452 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001453 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1454 f_sleep(0.5);
1455 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1456 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1457 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1458 mtc.stop;
1459 }
1460 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1461 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1462 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1463 mtc.stop;
1464 }
1465
1466 /* When the second leg is switched into bidirectional mode
1467 * as well, then the MGW will connect the two together and
1468 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001469 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1470 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001471 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001472
1473 if (crcx_osmux_wildcard) {
1474 /* For now we must set same CID as the MGW recvCID,
1475 * having sendCID!=recvCID is not yet supported. */
1476 flow[1].osmux_cid := flow[1].osmux_cid_response;
1477 }
1478 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1479 f_sleep(2.0);
1480
1481 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1482 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1483
1484 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1485 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1486 log("stats_rtp: ", stats_rtp);
1487 log("stats_osmux: ", stats_osmux);
1488 log("old_rtp_tx: ", num_pkts_tx[0]);
1489 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1490 mtc.stop;
1491 }
1492
1493 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1494 if (temp > 3 or temp < -3) {
1495 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1496 mtc.stop;
1497 }
1498
1499 f_rtpem_stats_err_check(stats_rtp);
1500 f_osmuxem_stats_err_check(stats_osmux);
1501
1502 /* Tear down */
1503 f_flow_delete(RTPEM[0]);
1504 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1505 setverdict(pass);
1506 }
1507
1508 /* create one RTP and one OSmux emulations and pass data in both
1509 directions. Create CRCX with wildcard Osmux CID and set it later
1510 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1511 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001512 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1513 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001514 }
1515
1516 /* create one RTP and one OSmux emulations and pass data in both
1517 directions. Create CRCX with fixed Osmux CID and keep it during
1518 MDCX. This is similar to how BSC sets up the call in AoIP. */
1519 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001520 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1521 mp_local_ipv4, mp_remote_ipv4);
1522 }
1523
1524 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1525 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1526 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1527 mp_local_ipv6, mp_remote_ipv6);
1528 }
1529 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1530 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1531 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1532 mp_local_ipv6, mp_remote_ipv6);
1533 }
1534 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1535 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1536 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1537 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001538 }
1539
Harald Welte646ecdb2017-12-28 03:21:57 +01001540 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1541 var template MgcpCommand cmd;
1542 var MgcpResponse resp;
1543
1544 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1545 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1546
1547 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1548
1549 setverdict(pass);
1550 }
1551
1552 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1553 var MgcpEndpoint ep;
1554 var MgcpCallId call_id;
1555 var integer ep_nr;
1556
1557 f_init();
1558
1559 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001560 if(ep_nr > 15) {
1561 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1562 } else {
1563 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1564 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001565 call_id := int2hex(ep_nr, 2) & '1234'H;
1566 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1567 }
1568 }
1569
Harald Welte79181ff2017-11-18 19:26:11 +01001570 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1571 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001572 var template MgcpCommand cmd;
1573 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001574 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001575 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001576
Harald Welteedc45c12017-11-18 19:15:05 +01001577 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001578
Harald Welteba62c8c2017-11-18 18:26:49 +01001579 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001580 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001581
Harald Welteba62c8c2017-11-18 18:26:49 +01001582 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001583
1584 setverdict(pass);
1585 }
1586
Harald Welte79181ff2017-11-18 19:26:11 +01001587 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1588 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1589 var template MgcpCommand cmd;
1590 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001591 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001592 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001593
1594 f_init(ep);
1595
1596 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1597 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1598
1599 f_dlcx_ok(ep);
1600
1601 setverdict(pass);
1602 }
1603
1604
Harald Welte6d167f82017-11-18 19:41:35 +01001605 /* CRCX + DLCX of valid endpoint but invalid call-id */
1606 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1607 var template MgcpCommand cmd;
1608 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001609 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001610 var MgcpCallId call_id := '51231'H;
1611
1612 f_init(ep);
1613
1614 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1615 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1616
1617 f_dlcx(ep, "516", *, 'ffff'H);
1618
1619 setverdict(pass);
1620 }
1621
1622
1623 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1624 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1625 var template MgcpCommand cmd;
1626 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001627 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001628 var MgcpCallId call_id := '51230'H;
1629
1630 f_init(ep);
1631
1632 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1633 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1634
1635 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1636
1637 setverdict(pass);
1638 }
1639
1640
Harald Weltee636afd2017-09-17 16:24:09 +08001641 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001642 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1643 var template MgcpCommand cmd;
1644 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001645 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001646 var MgcpCallId call_id := '51229'H;
1647 var template MgcpResponse rtmpl := {
1648 line := {
1649 code := "200",
1650 string := "OK"
1651 },
1652 params:= { },
1653 sdp := omit
1654 };
1655
1656 f_init(ep);
1657
1658 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1659 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1660
1661 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1662 resp := mgcp_transceive_mgw(cmd, rtmpl);
1663 resp := mgcp_transceive_mgw(cmd, rtmpl);
1664
1665 setverdict(pass);
1666 }
1667
Harald Weltebb7523b2018-03-29 08:52:01 +02001668 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1669 charstring codec) := {
1670 em := {
1671 hostname := host_a,
1672 portnr := omit
1673 },
1674 mgw := {
1675 hostname := host_b,
1676 portnr := omit
1677 },
1678 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001679 codec := codec,
1680 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001681 }
Harald Weltef53f1642017-11-18 19:57:11 +01001682
Harald Weltebb7523b2018-03-29 08:52:01 +02001683 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1684 testcase TC_rtpem_selftest() runs on dummy_CT {
1685 var RtpemStats stats[2];
1686 var integer local_port := 10000;
1687 var integer local_port2 := 20000;
1688
1689 f_init();
1690
1691 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1692 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1693
1694 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1695 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1696
1697 log("=== starting");
1698 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1699 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1700
1701 f_sleep(5.0);
1702
1703 log("=== stopping");
1704 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1705 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1706 f_sleep(0.5);
1707 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1708 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1709
1710 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1711 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1712 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1713 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001714 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001715 }
1716 setverdict(pass);
1717 }
1718
Philipp Maier2321ef92018-06-27 17:52:04 +02001719 /* Create one half open connection in receive-only mode. The MGW must accept
1720 * the packets but must not send any. */
1721 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1722 var RtpFlowData flow;
1723 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1724 var MgcpCallId call_id := '1225'H;
1725 var RtpemStats stats;
1726
1727 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001728 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001729 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001730 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001731
1732 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1733 f_sleep(1.0);
1734 f_flow_delete(RTPEM[0], ep, call_id);
1735
1736 stats := f_rtpem_stats_get(RTPEM[0]);
1737
Philipp Maier42b17cc2019-10-01 13:53:17 +02001738 /* Make sure that at least some amount of RTP packets/bytes
1739 * have has been transmitted. The compare values for
1740 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1741 * using a testrun and the results were devided by 2, so even
1742 * in load situations we should reach the minimum amount of
1743 * required packets/bytes */
1744
1745 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001746 setverdict(fail);
1747 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001748 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001749 setverdict(fail);
1750 }
Philipp Maier36291392018-07-25 09:40:44 +02001751
1752 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001753
1754 setverdict(pass);
1755 }
1756
1757 /* Create one connection in loopback mode, test if the RTP packets are
1758 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001759 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 +02001760 var RtpFlowData flow;
1761 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1762 var MgcpCallId call_id := '1225'H;
1763 var RtpemStats stats;
1764
1765 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001766 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001767 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001768 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001769
1770 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1771 f_sleep(1.0);
1772 f_flow_delete(RTPEM[0], ep, call_id);
1773
1774 stats := f_rtpem_stats_get(RTPEM[0]);
1775
1776 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1777 setverdict(fail);
1778 }
1779 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1780 setverdict(fail);
1781 }
Philipp Maier36291392018-07-25 09:40:44 +02001782
1783 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001784
1785 setverdict(pass);
1786 }
1787
Philipp Maier1ac13982021-05-07 23:06:07 +02001788 /* Create one connection in loopback mode, test if the RTP packets are
1789 * actually reflected */
1790 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001791 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001792 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001793 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1794 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1795 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001796
1797 /* Same as above, but we will intenionally not tell the MGW where to
1798 * send the outgoing traffic. The connection is still created in
1799 * loopback mode, so the MGW should take the originating address from
1800 * the incoming RTP packet and send it back to the source */
1801 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001802 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001803 }
1804
1805
Philipp Maier7df85f62018-07-25 10:26:09 +02001806 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1807 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001808 var RtpFlowData flow[2];
1809 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001810 var MgcpResponse resp;
1811 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1812 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001813 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001814
1815 f_init(ep);
1816
1817 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001818 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001819 /* bind local RTP emulation sockets */
1820 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001821 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001822
1823 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001824 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001825 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001826 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1827
1828 if (bidir) {
1829 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1830 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1831
1832 /* Note: When we test bidirectional we may
1833 * loose packets during switch off because
1834 * both ends are transmitting and we only
1835 * can switch them off one by one. */
1836 tolerance := 3;
1837 } else {
1838 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1839 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1840 }
1841
1842 f_sleep(1.0);
1843
1844 f_flow_delete(RTPEM[1]);
1845 f_flow_delete(RTPEM[0], ep, call_id);
1846
1847 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1848 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1849 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1850 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001851 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001852 }
1853
Philipp Maier36291392018-07-25 09:40:44 +02001854 f_rtpem_stats_err_check(stats[0]);
1855 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001856
Philipp Maier2321ef92018-06-27 17:52:04 +02001857 setverdict(pass);
1858 }
1859
1860 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1861 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001862 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001863 }
1864
1865 /* create two local RTP emulations; create two connections on MGW EP,
1866 * exchange some data in both directions */
1867 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001868 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1869 }
1870
1871 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1872 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1873 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1874 }
1875
1876 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1877 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1878 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001879 }
1880
1881 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001882 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1883 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001884 var RtpFlowData flow[2];
1885 var RtpemStats stats[2];
1886 var MgcpResponse resp;
1887 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1888 var MgcpCallId call_id := '1227'H;
1889 var integer num_pkts_tx[2];
1890 var integer temp;
1891
1892 f_init(ep);
1893
1894 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001895 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001896 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001897 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001898
1899 /* Create the second connection. This connection will be also
1900 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001901 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001902 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001903 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001904
1905 /* The first leg starts transmitting */
1906 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1907 f_sleep(0.5);
1908 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1909 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001910 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001911 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001912 }
1913 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1914 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001915 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001916 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001917 }
1918
1919 /* The second leg starts transmitting a little later */
1920 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1921 f_sleep(1.0);
1922 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1923 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001924 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001925 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001926 }
1927 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1928 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001929 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001930 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001931 }
1932
1933 /* The first leg will now be switched into bidirectional
1934 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001935 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1936 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1937 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001938 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1939 f_sleep(0.5);
1940 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001941 if (stats[0].num_pkts_rx_err_disabled != 0) {
1942 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001943 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001944 }
1945 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1946 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001947 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001948 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001949 }
1950
1951 /* When the second leg is switched into bidirectional mode
1952 * as well, then the MGW will connect the two together and
1953 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02001954 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1955 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001956 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001957 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1958 f_sleep(2.0);
1959
1960 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1961 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1962
1963 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1964 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001965 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001966 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001967 }
1968
1969 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1970 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001971 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001972 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001973 }
Philipp Maier36291392018-07-25 09:40:44 +02001974
1975 f_rtpem_stats_err_check(stats[0]);
1976 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001977
1978 /* Tear down */
1979 f_flow_delete(RTPEM[0]);
1980 f_flow_delete(RTPEM[1], ep, call_id);
1981 setverdict(pass);
1982 }
1983
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001984 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1985 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1986 mp_local_ipv4, mp_remote_ipv4);
1987 }
1988
1989 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
1990 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
1991 mp_local_ipv6, mp_remote_ipv6);
1992 }
1993
1994 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
1995 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1996 mp_local_ipv6, mp_remote_ipv6);
1997 }
1998
Philipp Maier2321ef92018-06-27 17:52:04 +02001999 /* Test what happens when two RTP streams from different sources target
2000 * a single connection. Is the unsolicited stream properly ignored? */
2001 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2002 var RtpFlowData flow[2];
2003 var RtpemStats stats[2];
2004 var MgcpResponse resp;
2005 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2006 var MgcpCallId call_id := '1234321326'H;
2007 var integer unsolicited_port := 10002;
2008
2009 f_init(ep);
2010
2011 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002012 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002013 /* bind local RTP emulation sockets */
2014 flow[0].em.portnr := 10000;
2015 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2016
2017 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002018 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002019 flow[1].em.portnr := 20000;
2020 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002021
2022 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2023 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2024
Philipp Maier2321ef92018-06-27 17:52:04 +02002025 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002026
Philipp Maier2321ef92018-06-27 17:52:04 +02002027 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002028 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2029 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002030 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2031
2032 f_sleep(0.5);
2033
Daniel Willmanna069d382018-12-13 13:53:33 +01002034 /* Stop transmitting packets and tear down the flows */
2035 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002036 f_flow_delete(RTPEM[0]);
2037 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002038
2039 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2040 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2041 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2042 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002043 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002044 }
2045
Philipp Maier36291392018-07-25 09:40:44 +02002046 f_rtpem_stats_err_check(stats[0]);
2047 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002048
Harald Weltebb7523b2018-03-29 08:52:01 +02002049 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002050 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002051
Philipp Maier2321ef92018-06-27 17:52:04 +02002052 /* Test a handover situation. We first create two connections transmit
2053 * some data bidirectionally. Then we will simulate a handover situation. */
2054 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2055 var RtpFlowData flow[2];
2056 var RtpemStats stats[3];
2057 var MgcpResponse resp;
2058 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2059 var MgcpCallId call_id := '76338'H;
2060 var integer port_old;
2061
2062 f_init(ep);
2063
2064 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002065 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002066 /* bind local RTP emulation sockets */
2067 flow[0].em.portnr := 10000;
2068 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2069
2070 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002071 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002072 flow[1].em.portnr := 20000;
2073 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2074
2075 /* Normal rtp flow for one second */
2076 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2077 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2078 f_sleep(1.0);
2079
2080 /* Now switch the flow over to a new port (BTS) */
2081 port_old := flow[0].em.portnr;
2082 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002083 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002084 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002085 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002086
2087 /* When handing over a call, the old source may still keep
2088 * transmitting for a while. We simulate this by injecting
2089 * some unsolicited packets on the behalf of the old source,
2090 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002091 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2092 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002093 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2094 f_sleep(1.0);
2095 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2096 f_sleep(1.0);
2097
2098 /* Terminate call */
2099 f_flow_delete(RTPEM[0]);
2100 f_flow_delete(RTPEM[1], ep, call_id);
2101
2102 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2103 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2104 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2105 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002106 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002107 }
2108 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2109 if (stats[2].num_pkts_rx_err_disabled != 0) {
2110 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002111 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002112 }
2113
Philipp Maier36291392018-07-25 09:40:44 +02002114 f_rtpem_stats_err_check(stats[0]);
2115 f_rtpem_stats_err_check(stats[1]);
2116 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002117
Philipp Maier2321ef92018-06-27 17:52:04 +02002118 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002119 }
Harald Weltef53f1642017-11-18 19:57:11 +01002120
Philipp Maier6d4e0942019-02-21 17:35:01 +01002121
2122 /* create two local RTP emulations; create two connections on MGW EP, see if
2123 * exchanged data is converted bwtween ts101318 and rfc5993 */
2124 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2125 var RtpFlowData flow[2];
2126 var RtpemStats stats[2];
2127 var MgcpResponse resp;
2128 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2129 var MgcpCallId call_id := '1226'H;
2130
2131 f_init(ep);
2132
2133 /* Turn on conversion mode */
2134 f_vty_enter_config(MGWVTY);
2135 f_vty_transceive(MGWVTY, "mgcp");
2136 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2137
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002138 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002139 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002140 /* bind local RTP emulation sockets */
2141 flow[0].em.portnr := 10000;
2142 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2143 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2144 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2145 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2146 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2147
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002148 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002149 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002150 flow[1].em.portnr := 20000;
2151 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2152 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2153 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2154 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2155 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2156
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002157 /* Send RTP packets to connection #0, receive on connection #1 */
2158 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2159 f_sleep(0.5);
2160 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002161 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002162 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2163 f_sleep(0.5);
2164 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002165
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002166 /* Send RTP packets to connection #1, receive on connection #0 */
2167 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2168 f_sleep(0.5);
2169 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2170 f_sleep(1.0);
2171 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2172 f_sleep(0.5);
2173 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2174
2175 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002176 f_flow_delete(RTPEM[0]);
2177 f_flow_delete(RTPEM[1], ep, call_id);
2178
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002179 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002180 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2181 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002182 f_rtpem_stats_err_check(stats[0]);
2183 f_rtpem_stats_err_check(stats[1]);
2184
2185 /* Turn off conversion mode */
2186 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2187
2188 setverdict(pass);
2189 }
2190
Philipp Maier4f764ce2019-03-07 10:54:10 +01002191 /* create two local RTP emulations; create two connections on MGW EP, see if
2192 * exchanged data is converted between AMR octet-aligned and bandwith
2193 * efficient-mode */
2194 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2195 var RtpFlowData flow[2];
2196 var RtpemStats stats[2];
2197 var MgcpResponse resp;
2198 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2199 var MgcpCallId call_id := '1226'H;
2200
2201 f_init(ep);
2202
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002203 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002204 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002205 /* bind local RTP emulation sockets */
2206 flow[0].em.portnr := 10000;
2207 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2208 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2209 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2210 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2211 flow[0].fmtp := fmtp0;
2212 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2213
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002214 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002215 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002216 flow[1].em.portnr := 20000;
2217 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2218 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2219 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2220 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2221 flow[1].fmtp := fmtp1;
2222 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2223
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002224 /* Send RTP packets to connection #0, receive on connection #1 */
2225 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2226 f_sleep(0.5);
2227 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002228 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002229 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2230 f_sleep(0.5);
2231 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002232
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002233 /* Send RTP packets to connection #1, receive on connection #0 */
2234 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2235 f_sleep(0.5);
2236 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2237 f_sleep(1.0);
2238 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2239 f_sleep(0.5);
2240 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2241
2242 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002243 f_flow_delete(RTPEM[0]);
2244 f_flow_delete(RTPEM[1], ep, call_id);
2245
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002246 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002247 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2248 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002249 f_rtpem_stats_err_check(stats[0]);
2250 f_rtpem_stats_err_check(stats[1]);
2251
2252 setverdict(pass);
2253 }
2254
Philipp Maier882843d2020-05-25 15:33:13 +02002255 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2256 * functions are real world AMR RTP payloads including AMR header. The
2257 * payloads were extracted from a trace with known good payloads. */
2258
Philipp Maier4f764ce2019-03-07 10:54:10 +01002259 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002260 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002261 }
2262
2263 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2264 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2265 }
2266
2267 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2268 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2269 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002270
Harald Weltee636afd2017-09-17 16:24:09 +08002271 /* TODO: Double-DLCX (no retransmission) */
2272
2273
2274
2275 /* TODO: AUEP (various) */
2276 /* TODO: RSIP (various) */
2277 /* TODO: RQNT (various) */
2278 /* TODO: EPCF (various) */
2279 /* TODO: AUCX (various) */
2280 /* TODO: invalid verb (various) */
2281
Oliver Smith021141e2019-06-25 12:09:01 +02002282
2283 testcase TC_conn_timeout() runs on dummy_CT {
2284 var RtpFlowData flow;
2285 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2286 var MgcpCallId call_id := '1225'H;
2287 var MGCP_RecvFrom mrf;
2288
2289 f_init(ep);
2290 log("Setting conn-timeout to 1s");
2291 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2292
2293 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002294 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002295 flow.em.portnr := 10000;
2296 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2297 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2298 f_sleep(1.5);
2299
2300 log("Stopping for 0.5s and resuming");
2301 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2302 f_sleep(0.5);
2303 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2304 f_sleep(0.1);
2305
2306 log("Stopping for 1.5s, expecting to run into timeout");
2307 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2308 f_sleep(1.5);
2309
2310 log("Resuming should fail now");
2311 f_rtpem_conn_refuse_expect(RTPEM[0]);
2312 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2313 f_sleep(0.2);
2314 f_rtpem_conn_refuse_verify(RTPEM[0]);
2315
2316 setverdict(pass);
2317 }
2318
Philipp Maier2609c752020-07-08 12:38:09 +02002319 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2320 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2321 var template MgcpCommand cmd;
2322 var MgcpResponse resp;
2323 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2324 var MgcpCallId call_id := '8376F297'H;
2325
2326 f_init(ep);
2327
2328 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2329 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2330
2331 f_dlcx_ok(ep);
2332
2333 setverdict(pass);
2334 }
2335
2336 /* Test what happens when overlapping endpoints are selected (E1) */
2337 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2338 var template MgcpCommand cmd;
2339 var MgcpResponse resp;
2340 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2341 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2342 var MgcpCallId call_id_1 := '8376F297'H;
2343 var MgcpCallId call_id_2 := '837AF2A7'H;
2344
2345 f_init();
2346
2347 /* ep_1 and ep_2 are overlapping, selecting both one after
2348 * another should work fine: */
2349 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2350 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2351 f_dlcx_ok(ep_1);
2352 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2353 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2354 f_dlcx_ok(ep_2);
2355
2356 /* When ep_1 is serving a call we can not select ep_2 becaus
2357 * it is overlapping with ep_1 */
2358 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2359 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2360 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2361 resp := mgcp_transceive_mgw(cmd, ?);
2362 if (resp.line.code != "501") {
2363 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2364 }
2365 f_dlcx_ok(ep_1);
2366
2367 setverdict(pass);
2368 }
2369
2370 /* Create one connection in loopback mode, test if the RTP packets are
2371 * actually reflected */
2372 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2373 var RtpFlowData flow;
2374 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2375 var MgcpCallId call_id := '12250989'H;
2376 var RtpemStats stats;
2377
2378 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002379 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002380 flow.em.portnr := 10000;
2381 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2382
2383 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2384 f_sleep(1.0);
2385 f_flow_delete(RTPEM[0], ep, call_id);
2386
2387 stats := f_rtpem_stats_get(RTPEM[0]);
2388
2389 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2390 setverdict(fail);
2391 }
2392 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2393 setverdict(fail);
2394 }
2395
2396 f_rtpem_stats_err_check(stats);
2397
2398 setverdict(pass);
2399 }
2400
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002401 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2402 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2403 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2404 var template MgcpCommand cmd;
2405 var MgcpResponse resp;
2406 var MgcpCallId call_id := '1234'H;
2407 var MgcpConnectionId conn_id;
2408
2409 f_init(ep);
2410
2411 /* create the connection on the MGW */
2412 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2413 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2414 conn_id := extract_conn_id(resp);
2415
2416 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2417 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2418 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2419 valueof(ts_SDP_ptime(20)) });
2420 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2421
2422 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2423 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2424 }
2425 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2426 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2427 }
2428
2429 /* clean-up */
2430 f_dlcx_ok(ep, call_id);
2431 setverdict(pass);
2432 }
2433
2434 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2435 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2436 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2437 var template MgcpCommand cmd;
2438 var MgcpResponse resp;
2439 var MgcpCallId call_id := '1234'H;
2440 var MgcpConnectionId conn_id;
2441
2442 f_init(ep);
2443
2444 /* create the connection on the MGW */
2445 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2446 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2447 conn_id := extract_conn_id(resp);
2448
2449 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2450 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2451 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2452 valueof(ts_SDP_ptime(20)) });
2453 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2454
2455 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2456 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2457 }
2458 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2459 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2460 }
2461
2462 /* clean-up */
2463 f_dlcx_ok(ep, call_id);
2464 setverdict(pass);
2465 }
2466
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002467 /* create two local RTP+IuUP emulations and pass data in both directions */
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002468 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002469 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002470 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2471 var RtpFlowData flow[2];
2472 var RtpemStats stats[2];
2473 var MgcpResponse resp;
2474 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2475 var MgcpCallId call_id := '1227'H;
2476 var integer num_pkts_tx[2];
2477 var integer temp;
2478
2479 f_init(ep);
2480
2481 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2482 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2483 flow[0].em.portnr := 10000;
2484 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2485 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2486 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002487 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002488 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002489 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2490 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2491
2492 /* Create the second connection. This connection will be also
2493 * in receive only mode (CN side, IuUP-Init passive) */
2494 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2495 flow[1].em.portnr := 20000;
2496 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2497 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2498 flow[1].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002499 flow[1].rtp_cfg.iuup_cfg.active_init := false;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002500 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2501 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2502
2503 /* The first leg starts transmitting */
2504 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2505 f_sleep(0.5);
2506 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2507 if (stats[0].num_pkts_rx_err_disabled != 0) {
2508 setverdict(fail, "received packets from MGW on recvonly connection 0");
2509 mtc.stop;
2510 }
2511 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2512 if (stats[1].num_pkts_rx_err_disabled != 0) {
2513 setverdict(fail, "received packets from MGW on recvonly connection 1");
2514 mtc.stop;
2515 }
2516
2517 /* The second leg starts transmitting a little later */
2518 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2519 f_sleep(1.0);
2520 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2521 if (stats[0].num_pkts_rx_err_disabled != 0) {
2522 setverdict(fail, "received packets from MGW on recvonly connection 0");
2523 mtc.stop;
2524 }
2525 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2526 if (stats[1].num_pkts_rx_err_disabled != 0) {
2527 setverdict(fail, "received packets from MGW on recvonly connection 1");
2528 mtc.stop;
2529 }
2530
2531 /* The first leg will now be switched into bidirectional
2532 * mode, but we do not expect any data coming back yet. */
2533 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2534 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2535 num_pkts_tx[1] := stats[1].num_pkts_tx;
2536 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2537 f_sleep(0.5);
2538 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2539 if (stats[0].num_pkts_rx_err_disabled != 0) {
2540 setverdict(fail, "received packets from MGW on recvonly connection 0");
2541 mtc.stop;
2542 }
2543 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2544 if (stats[1].num_pkts_rx_err_disabled != 0) {
2545 setverdict(fail, "received packets from MGW on recvonly connection 1");
2546 mtc.stop;
2547 }
2548
2549 /* When the second leg is switched into bidirectional mode
2550 * as well, then the MGW will connect the two together and
2551 * we should see RTP streams passing through from both ends. */
2552 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2553 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2554 num_pkts_tx[0] := stats[0].num_pkts_tx;
2555 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2556 f_sleep(2.0);
2557
2558 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2559 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2560
2561 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2562 if (temp > 3 or temp < -3) {
2563 setverdict(fail, "number of packets not within normal parameters:", temp);
2564 mtc.stop;
2565 }
2566
2567 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2568 if (temp > 3 or temp < -3) {
2569 setverdict(fail, "number of packets not within normal parameters:", temp);
2570 mtc.stop;
2571 }
2572
2573 f_rtpem_stats_err_check(stats[0]);
2574 f_rtpem_stats_err_check(stats[1]);
2575
2576 /* Tear down */
2577 f_flow_delete(RTPEM[0]);
2578 f_flow_delete(RTPEM[1], ep, call_id);
2579 setverdict(pass);
2580 }
2581 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002582 var template (value) IuUP_RabFlowCombinationList rfcl := {
2583 t_IuUP_RFC_AMR_12_2(0),
2584 t_IuUP_RFC_AMR_SID(1),
2585 t_IuUP_RFC_AMR_NO_DATA(2)
2586 };
2587 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2588 mp_local_ipv4, mp_remote_ipv4);
2589 }
2590 /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */
2591 testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT {
2592 var template (value) IuUP_RabFlowCombinationList rfcl := {
2593 t_IuUP_RFC_AMR_12_2(1),
2594 t_IuUP_RFC_AMR_SID(2),
2595 t_IuUP_RFC_AMR_NO_DATA(0)
2596 };
2597 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002598 mp_local_ipv4, mp_remote_ipv4);
2599 }
2600
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002601 /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */
2602 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 +02002603 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002604 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2605 var RtpFlowData flow[2];
2606 var RtpemStats stats[2];
2607 var MgcpResponse resp;
2608 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2609 var MgcpCallId call_id := '1227'H;
2610 var integer num_pkts_tx[2];
2611 var integer temp;
2612
2613 f_init(ep);
2614
2615 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2616 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2617 flow[0].em.portnr := 10000;
2618 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2619 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2620 flow[0].rtp_cfg.tx_fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
2621 flow[0].rtp_cfg.rx_fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O; /* flow[1].rtp_cfg.tx_fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
2622 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002623 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002624 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002625 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2626 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2627
2628 /* Create the second connection. This connection will be also
2629 * in receive only mode (CN side, regular RTP) */
2630 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
2631 flow[1].em.portnr := 20000;
2632 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2633 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2634 flow[1].rtp_cfg.tx_fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
2635 flow[1].rtp_cfg.rx_fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O; /* flow[0].rtp_cfg.tx_fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
2636 flow[1].rtp_cfg.iuup_mode := false;
2637 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2638 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2639
2640 /* The first leg starts transmitting */
2641 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2642 f_sleep(0.5);
2643 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2644 if (stats[0].num_pkts_rx_err_disabled != 0) {
2645 setverdict(fail, "received packets from MGW on recvonly connection 0");
2646 mtc.stop;
2647 }
2648 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2649 if (stats[1].num_pkts_rx_err_disabled != 0) {
2650 setverdict(fail, "received packets from MGW on recvonly connection 1");
2651 mtc.stop;
2652 }
2653
2654 /* The second leg starts transmitting a little later */
2655 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2656 f_sleep(1.0);
2657 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2658 if (stats[0].num_pkts_rx_err_disabled != 0) {
2659 setverdict(fail, "received packets from MGW on recvonly connection 0");
2660 mtc.stop;
2661 }
2662 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2663 if (stats[1].num_pkts_rx_err_disabled != 0) {
2664 setverdict(fail, "received packets from MGW on recvonly connection 1");
2665 mtc.stop;
2666 }
2667
2668 /* The first leg will now be switched into bidirectional
2669 * mode, but we do not expect any data coming back yet. */
2670 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2671 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2672 num_pkts_tx[1] := stats[1].num_pkts_tx;
2673 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2674 f_sleep(0.5);
2675 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2676 if (stats[0].num_pkts_rx_err_disabled != 0) {
2677 setverdict(fail, "received packets from MGW on recvonly connection 0");
2678 mtc.stop;
2679 }
2680 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2681 if (stats[1].num_pkts_rx_err_disabled != 0) {
2682 setverdict(fail, "received packets from MGW on recvonly connection 1");
2683 mtc.stop;
2684 }
2685
2686 /* When the second leg is switched into bidirectional mode
2687 * as well, then the MGW will connect the two together and
2688 * we should see RTP streams passing through from both ends. */
2689 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2690 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2691 num_pkts_tx[0] := stats[0].num_pkts_tx;
2692 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2693 f_sleep(2.0);
2694
2695 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2696 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2697
2698 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2699 if (temp > 3 or temp < -3) {
2700 setverdict(fail, "number of packets not within normal parameters:", temp);
2701 mtc.stop;
2702 }
2703
2704 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2705 if (temp > 3 or temp < -3) {
2706 setverdict(fail, "number of packets not within normal parameters:", temp);
2707 mtc.stop;
2708 }
2709
2710 f_rtpem_stats_err_check(stats[0]);
2711 f_rtpem_stats_err_check(stats[1]);
2712
2713 /* Tear down */
2714 f_flow_delete(RTPEM[0]);
2715 f_flow_delete(RTPEM[1], ep, call_id);
2716 setverdict(pass);
2717 }
2718 testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002719 var template (value) IuUP_RabFlowCombinationList rfcl := {
2720 t_IuUP_RFC_AMR_12_2(0),
2721 t_IuUP_RFC_AMR_SID(1),
2722 t_IuUP_RFC_AMR_NO_DATA(2)
2723 };
2724 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2725 mp_local_ipv4, mp_remote_ipv4);
2726 }
2727 /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */
2728 testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT {
2729 var template (value) IuUP_RabFlowCombinationList rfcl := {
2730 t_IuUP_RFC_AMR_12_2(1),
2731 t_IuUP_RFC_AMR_SID(2),
2732 t_IuUP_RFC_AMR_NO_DATA(0)
2733 };
2734 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002735 mp_local_ipv4, mp_remote_ipv4);
2736 }
2737
Harald Welte00a067f2017-09-13 23:27:17 +02002738 control {
2739 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002740 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002741 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002742 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002743 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002744 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002745 execute(TC_crcx_early_bidir_mode());
2746 execute(TC_crcx_unsupp_param());
2747 execute(TC_crcx_missing_callid());
2748 execute(TC_crcx_missing_mode());
2749 execute(TC_crcx_unsupp_packet_intv());
2750 execute(TC_crcx_illegal_double_lco());
2751 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002752 execute(TC_crcx_wildcarded());
2753 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002754 execute(TC_mdcx_without_crcx());
2755 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02002756 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002757 execute(TC_mdcx_wildcarded());
2758 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002759 execute(TC_crcx_and_dlcx_ep_callid_connid());
2760 execute(TC_crcx_and_dlcx_ep_callid());
2761 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002762 execute(TC_crcx_and_dlcx_ep_callid_inval());
2763 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002764 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002765
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002766 execute(TC_crcx_osmux_wildcard());
2767 execute(TC_crcx_osmux_fixed());
2768 execute(TC_crcx_osmux_fixed_twice());
2769 execute(TC_one_crcx_receive_only_osmux());
2770 execute(TC_one_crcx_loopback_osmux());
2771 execute(TC_two_crcx_and_rtp_osmux());
2772 execute(TC_two_crcx_and_rtp_osmux_bidir());
2773 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2774 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2775
Harald Welte33d82162017-12-28 03:21:57 +01002776 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002777
2778 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002779
2780 execute(TC_one_crcx_receive_only_rtp());
2781 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002782 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02002783 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002784 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002785 execute(TC_two_crcx_diff_pt_and_rtp());
2786 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002787 execute(TC_two_crcx_mdcx_and_rtp());
2788 execute(TC_two_crcx_and_unsolicited_rtp());
2789 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002790 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002791 execute(TC_amr_oa_bwe_rtp_conversion());
2792 execute(TC_amr_oa_oa_rtp_conversion());
2793 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002794
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002795 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002796
2797 execute(TC_e1_crcx_and_dlcx_ep());
2798 execute(TC_e1_crcx_with_overlap());
2799 execute(TC_e1_crcx_loopback());
2800
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002801 execute(TC_crcx_mdcx_ip4());
2802 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002803 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2804 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2805 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2806 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2807 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2808 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2809 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2810 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02002811
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002812 execute(TC_two_crcx_mdcx_and_iuup());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002813 execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered());
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002814 execute(TC_two_crcx_mdcx_and_iuup_rtp());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002815 execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered());
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002816
Philipp Maier37965082021-05-25 16:44:25 +02002817 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
2818 * older versions of osmo-mgw. This eventually leads into
2819 * a failure of all subsequent testcases, so it is important
2820 * not to add new testcaes after this one. */
2821 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02002822 }
2823}