blob: 0242db9cba419e5c182324275f4c1e85fe83a831 [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 Maier6c740e82022-06-30 12:04:34 +02001121 /* Query a the statsd once to ensure that intermediate results are pulled from the
1122 * pipeline. The second query (below) will return the actual result. */
1123 expect := {
1124 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := n_endpoints}
1125 };
1126 f_statsd_expect(expect);
1127
1128 /* The second query must resturn a result with 0 endpoints in use. */
Philipp Maier55b90542021-07-02 12:33:19 +02001129 expect := {
1130 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1131 };
1132 f_statsd_expect(expect);
1133
Philipp Maier8a3dc922018-02-02 14:55:12 +01001134 setverdict(pass);
1135 }
1136
Harald Welte79181ff2017-11-18 19:26:11 +01001137 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1138 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1139 var template MgcpCommand cmd;
1140 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001141 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001142 var MgcpCallId call_id := '51234'H;
1143
1144 f_init(ep);
1145
1146 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1147 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1148
1149 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1150
1151 setverdict(pass);
1152 }
1153
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001154 /* test valid CRCX without SDP */
1155 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1156 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1157 setverdict(pass);
1158 }
1159
1160 /* test valid CRCX without SDP */
1161 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1162 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1163 setverdict(pass);
1164 }
1165
1166 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1167 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1168 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1169 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1170 setverdict(pass);
1171 }
1172
1173 /* Create one half open connection in receive-only mode. The MGW must accept
1174 * the packets but must not send any. */
1175 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1176 var RtpFlowData flow;
1177 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1178 var MgcpCallId call_id := '1225'H;
1179 var OsmuxemStats stats;
1180 var OsmuxTxHandle tx_hdl;
1181
1182 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001183 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001184 flow.em.portnr := mp_local_osmux_port;
1185 flow.osmux_cid := -1;
1186 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1187
1188 /* create a transmitter not yet known by MGW */
1189 tx_hdl := valueof(t_TxHandleAMR590(2));
1190 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1191
1192 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1193 f_sleep(1.0);
1194 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1195
1196 stats := f_osmuxem_stats_get(OsmuxEM);
1197
1198 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1199 setverdict(fail);
1200 }
1201 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1202 setverdict(fail);
1203 }
1204
1205 f_osmuxem_stats_err_check(stats);
1206
1207 setverdict(pass);
1208 }
1209
1210 /* Create one connection in loopback mode, test if the Osmux packets are
1211 * actually reflected */
1212 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1213 var RtpFlowData flow;
1214 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1215 var MgcpCallId call_id := '1225'H;
1216 var OsmuxemStats stats;
1217 var OsmuxTxHandle tx_hdl;
1218
1219 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001220 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001221 flow.em.portnr := mp_local_osmux_port;
1222 flow.osmux_cid := 2;
1223 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1224
1225 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1226 f_sleep(1.0);
1227
1228 /* Switch off both Tx, wait to receive delayed frames from MGW */
1229 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1230 f_sleep(0.1);
1231 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1232
1233 stats := f_osmuxem_stats_get(OsmuxEM);
1234
1235 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1236 setverdict(fail);
1237 }
1238 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1239 setverdict(fail);
1240 }
1241
1242 f_osmuxem_stats_err_check(stats);
1243
1244 setverdict(pass);
1245 }
1246
1247 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1248 * must match the reception statistics on the other side and vice versa. The
1249 * user may also supply a tolerance value (number of packets) when deviations
1250 * are acceptable */
1251 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1252 var integer plen;
1253
1254 log("stats A: ", a);
1255 log("stats B: ", b);
1256 log("tolerance: ", tolerance, " packets");
1257 log("batch_size: ", batch_size, " packets");
1258
1259 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1260
1261 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1262 return false;
1263 }
1264
1265 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1266 return false;
1267 }
1268
1269 if(a.num_pkts_tx > 0) {
1270 plen := a.bytes_payload_tx / a.num_pkts_tx;
1271 } else {
1272 plen := 0;
1273 }
1274
1275 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1276 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1277 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1278 return false;
1279 }
1280
1281 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) {
1282 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1283 return false;
1284 }
1285
1286 return true;
1287 }
1288
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001289 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1290 charstring local_ip_rtp, charstring remote_ip_rtp,
1291 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001292 var RtpFlowData flow[2];
1293 var RtpemStats stats_rtp;
1294 var OsmuxemStats stats_osmux;
1295 var MgcpResponse resp;
1296 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1297 var MgcpCallId call_id := '1226'H;
1298 var integer tolerance := 0;
1299
1300 f_init(ep, true);
1301
1302 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001303 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001304 flow[0].rtp_cfg := c_RtpemDefaultCfg
1305 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1306 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1307 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);
1308 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1309 /* bind local RTP emulation sockets */
1310 flow[0].em.portnr := 10000;
1311 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1312
1313 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001314 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001315 flow[1].em.portnr := mp_local_osmux_port;
1316 flow[1].osmux_cid := 2;
1317 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1318 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1319
1320 if (bidir) {
1321 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1322 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1323
1324 /* Note: When we test bidirectional we may
1325 * loose packets during switch off because
1326 * both ends are transmitting and we only
1327 * can switch them off one by one. */
1328 tolerance := 3;
1329 } else {
1330 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1331 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1332 }
1333
1334 f_sleep(1.0);
1335
1336 /* Switch off both Tx, wait to receive delayed frames from MGW */
1337 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1338 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1339 f_sleep(0.1);
1340
1341 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1342 f_flow_delete(RTPEM[1]);
1343
1344 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1345 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1346 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1347 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1348 mtc.stop;
1349 }
1350
1351 f_rtpem_stats_err_check(stats_rtp);
1352 f_osmuxem_stats_err_check(stats_osmux);
1353
1354 setverdict(pass);
1355 }
1356
1357 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1358 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001359 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1360 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001361 }
1362
1363 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1364 * exchange some data in both directions */
1365 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001366 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1367 mp_local_ipv4, mp_remote_ipv4);
1368 }
1369
1370 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1371 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1372 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1373 mp_local_ipv6, mp_remote_ipv6);
1374 }
1375 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1376 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1377 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1378 mp_local_ipv6, mp_remote_ipv6);
1379 }
1380 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1381 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1382 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1383 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001384 }
1385
1386
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001387 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1388 charstring local_ip_rtp, charstring remote_ip_rtp,
1389 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001390 var RtpFlowData flow[2];
1391 var RtpemStats stats_rtp;
1392 var OsmuxemStats stats_osmux;
1393 var MgcpResponse resp;
1394 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1395 var MgcpCallId call_id := '1227'H;
1396 var integer num_pkts_tx[2];
1397 var integer temp;
1398
1399 f_init(ep, true);
1400
1401 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001402 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001403 flow[0].rtp_cfg := c_RtpemDefaultCfg
1404 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1405 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1406 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);
1407 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1408 /* bind local RTP emulation sockets */
1409 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001410 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001411
1412
1413 /* Create the second connection. This connection will be also
1414 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001415 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001416 flow[1].em.portnr := mp_local_osmux_port;
1417 if (crcx_osmux_wildcard) {
1418 flow[1].osmux_cid := -1;
1419 } else {
1420 flow[1].osmux_cid := 2;
1421 }
1422 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001423 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001424
1425
1426 /* The first leg starts transmitting */
1427 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1428 f_sleep(0.5);
1429 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1430 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1431 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1432 mtc.stop;
1433 }
1434 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1435 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1436 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1437 mtc.stop;
1438 }
1439
1440 /* The second leg starts transmitting a little later */
1441 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1442 f_sleep(1.0);
1443 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1444 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1445 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1446 mtc.stop;
1447 }
1448 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1449 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1450 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1451 mtc.stop;
1452 }
1453
1454 /* The first leg will now be switched into bidirectional
1455 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001456 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1457 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1458 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001459 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1460 f_sleep(0.5);
1461 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1462 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1463 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1464 mtc.stop;
1465 }
1466 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1467 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1468 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1469 mtc.stop;
1470 }
1471
1472 /* When the second leg is switched into bidirectional mode
1473 * as well, then the MGW will connect the two together and
1474 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001475 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1476 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001477 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001478
1479 if (crcx_osmux_wildcard) {
1480 /* For now we must set same CID as the MGW recvCID,
1481 * having sendCID!=recvCID is not yet supported. */
1482 flow[1].osmux_cid := flow[1].osmux_cid_response;
1483 }
1484 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1485 f_sleep(2.0);
1486
1487 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1488 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1489
1490 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1491 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1492 log("stats_rtp: ", stats_rtp);
1493 log("stats_osmux: ", stats_osmux);
1494 log("old_rtp_tx: ", num_pkts_tx[0]);
1495 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1496 mtc.stop;
1497 }
1498
1499 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1500 if (temp > 3 or temp < -3) {
1501 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1502 mtc.stop;
1503 }
1504
1505 f_rtpem_stats_err_check(stats_rtp);
1506 f_osmuxem_stats_err_check(stats_osmux);
1507
1508 /* Tear down */
1509 f_flow_delete(RTPEM[0]);
1510 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1511 setverdict(pass);
1512 }
1513
1514 /* create one RTP and one OSmux emulations and pass data in both
1515 directions. Create CRCX with wildcard Osmux CID and set it later
1516 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1517 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001518 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1519 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001520 }
1521
1522 /* create one RTP and one OSmux emulations and pass data in both
1523 directions. Create CRCX with fixed Osmux CID and keep it during
1524 MDCX. This is similar to how BSC sets up the call in AoIP. */
1525 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001526 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1527 mp_local_ipv4, mp_remote_ipv4);
1528 }
1529
1530 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1531 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1532 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1533 mp_local_ipv6, mp_remote_ipv6);
1534 }
1535 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1536 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1537 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1538 mp_local_ipv6, mp_remote_ipv6);
1539 }
1540 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1541 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1542 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1543 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001544 }
1545
Harald Welte646ecdb2017-12-28 03:21:57 +01001546 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1547 var template MgcpCommand cmd;
1548 var MgcpResponse resp;
1549
1550 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1551 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1552
1553 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1554
1555 setverdict(pass);
1556 }
1557
1558 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1559 var MgcpEndpoint ep;
1560 var MgcpCallId call_id;
1561 var integer ep_nr;
1562
1563 f_init();
1564
1565 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001566 if(ep_nr > 15) {
1567 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1568 } else {
1569 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1570 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001571 call_id := int2hex(ep_nr, 2) & '1234'H;
1572 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1573 }
1574 }
1575
Harald Welte79181ff2017-11-18 19:26:11 +01001576 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1577 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001578 var template MgcpCommand cmd;
1579 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001580 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001581 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001582
Harald Welteedc45c12017-11-18 19:15:05 +01001583 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001584
Harald Welteba62c8c2017-11-18 18:26:49 +01001585 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001586 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001587
Harald Welteba62c8c2017-11-18 18:26:49 +01001588 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001589
1590 setverdict(pass);
1591 }
1592
Harald Welte79181ff2017-11-18 19:26:11 +01001593 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1594 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1595 var template MgcpCommand cmd;
1596 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001597 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001598 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001599
1600 f_init(ep);
1601
1602 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1603 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1604
1605 f_dlcx_ok(ep);
1606
1607 setverdict(pass);
1608 }
1609
1610
Harald Welte6d167f82017-11-18 19:41:35 +01001611 /* CRCX + DLCX of valid endpoint but invalid call-id */
1612 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1613 var template MgcpCommand cmd;
1614 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001615 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001616 var MgcpCallId call_id := '51231'H;
1617
1618 f_init(ep);
1619
1620 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1621 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1622
1623 f_dlcx(ep, "516", *, 'ffff'H);
1624
1625 setverdict(pass);
1626 }
1627
1628
1629 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1630 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1631 var template MgcpCommand cmd;
1632 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001633 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001634 var MgcpCallId call_id := '51230'H;
1635
1636 f_init(ep);
1637
1638 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1639 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1640
1641 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1642
1643 setverdict(pass);
1644 }
1645
1646
Harald Weltee636afd2017-09-17 16:24:09 +08001647 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001648 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1649 var template MgcpCommand cmd;
1650 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001651 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001652 var MgcpCallId call_id := '51229'H;
1653 var template MgcpResponse rtmpl := {
1654 line := {
1655 code := "200",
1656 string := "OK"
1657 },
1658 params:= { },
1659 sdp := omit
1660 };
1661
1662 f_init(ep);
1663
1664 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1665 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1666
1667 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1668 resp := mgcp_transceive_mgw(cmd, rtmpl);
1669 resp := mgcp_transceive_mgw(cmd, rtmpl);
1670
1671 setverdict(pass);
1672 }
1673
Harald Weltebb7523b2018-03-29 08:52:01 +02001674 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1675 charstring codec) := {
1676 em := {
1677 hostname := host_a,
1678 portnr := omit
1679 },
1680 mgw := {
1681 hostname := host_b,
1682 portnr := omit
1683 },
1684 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001685 codec := codec,
1686 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001687 }
Harald Weltef53f1642017-11-18 19:57:11 +01001688
Harald Weltebb7523b2018-03-29 08:52:01 +02001689 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1690 testcase TC_rtpem_selftest() runs on dummy_CT {
1691 var RtpemStats stats[2];
1692 var integer local_port := 10000;
1693 var integer local_port2 := 20000;
1694
1695 f_init();
1696
1697 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1698 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1699
1700 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1701 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1702
1703 log("=== starting");
1704 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1705 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1706
1707 f_sleep(5.0);
1708
1709 log("=== stopping");
1710 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1711 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1712 f_sleep(0.5);
1713 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1714 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1715
1716 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1717 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1718 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1719 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001720 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001721 }
1722 setverdict(pass);
1723 }
1724
Philipp Maier2321ef92018-06-27 17:52:04 +02001725 /* Create one half open connection in receive-only mode. The MGW must accept
1726 * the packets but must not send any. */
1727 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1728 var RtpFlowData flow;
1729 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1730 var MgcpCallId call_id := '1225'H;
1731 var RtpemStats stats;
1732
1733 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001734 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001735 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001736 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001737
1738 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1739 f_sleep(1.0);
1740 f_flow_delete(RTPEM[0], ep, call_id);
1741
1742 stats := f_rtpem_stats_get(RTPEM[0]);
1743
Philipp Maier42b17cc2019-10-01 13:53:17 +02001744 /* Make sure that at least some amount of RTP packets/bytes
1745 * have has been transmitted. The compare values for
1746 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1747 * using a testrun and the results were devided by 2, so even
1748 * in load situations we should reach the minimum amount of
1749 * required packets/bytes */
1750
1751 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001752 setverdict(fail);
1753 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001754 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001755 setverdict(fail);
1756 }
Philipp Maier36291392018-07-25 09:40:44 +02001757
1758 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001759
1760 setverdict(pass);
1761 }
1762
1763 /* Create one connection in loopback mode, test if the RTP packets are
1764 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001765 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 +02001766 var RtpFlowData flow;
1767 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1768 var MgcpCallId call_id := '1225'H;
1769 var RtpemStats stats;
1770
1771 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001772 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001773 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001774 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001775
1776 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1777 f_sleep(1.0);
1778 f_flow_delete(RTPEM[0], ep, call_id);
1779
1780 stats := f_rtpem_stats_get(RTPEM[0]);
1781
1782 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1783 setverdict(fail);
1784 }
1785 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1786 setverdict(fail);
1787 }
Philipp Maier36291392018-07-25 09:40:44 +02001788
1789 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001790
1791 setverdict(pass);
1792 }
1793
Philipp Maier1ac13982021-05-07 23:06:07 +02001794 /* Create one connection in loopback mode, test if the RTP packets are
1795 * actually reflected */
1796 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001797 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001798 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001799 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1800 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1801 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001802
1803 /* Same as above, but we will intenionally not tell the MGW where to
1804 * send the outgoing traffic. The connection is still created in
1805 * loopback mode, so the MGW should take the originating address from
1806 * the incoming RTP packet and send it back to the source */
1807 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001808 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001809 }
1810
1811
Philipp Maier7df85f62018-07-25 10:26:09 +02001812 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1813 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001814 var RtpFlowData flow[2];
1815 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001816 var MgcpResponse resp;
1817 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1818 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001819 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001820
1821 f_init(ep);
1822
1823 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001824 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001825 /* bind local RTP emulation sockets */
1826 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001827 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001828
1829 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001830 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001831 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001832 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1833
1834 if (bidir) {
1835 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1836 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1837
1838 /* Note: When we test bidirectional we may
1839 * loose packets during switch off because
1840 * both ends are transmitting and we only
1841 * can switch them off one by one. */
1842 tolerance := 3;
1843 } else {
1844 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1845 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1846 }
1847
1848 f_sleep(1.0);
1849
1850 f_flow_delete(RTPEM[1]);
1851 f_flow_delete(RTPEM[0], ep, call_id);
1852
1853 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1854 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1855 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1856 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001857 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001858 }
1859
Philipp Maier36291392018-07-25 09:40:44 +02001860 f_rtpem_stats_err_check(stats[0]);
1861 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001862
Philipp Maier2321ef92018-06-27 17:52:04 +02001863 setverdict(pass);
1864 }
1865
1866 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1867 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001868 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001869 }
1870
1871 /* create two local RTP emulations; create two connections on MGW EP,
1872 * exchange some data in both directions */
1873 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001874 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1875 }
1876
1877 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1878 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1879 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1880 }
1881
1882 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1883 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1884 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001885 }
1886
1887 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001888 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1889 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001890 var RtpFlowData flow[2];
1891 var RtpemStats stats[2];
1892 var MgcpResponse resp;
1893 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1894 var MgcpCallId call_id := '1227'H;
1895 var integer num_pkts_tx[2];
1896 var integer temp;
1897
1898 f_init(ep);
1899
1900 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001901 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001902 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001903 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001904
1905 /* Create the second connection. This connection will be also
1906 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001907 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001908 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001909 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001910
1911 /* The first leg starts transmitting */
1912 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1913 f_sleep(0.5);
1914 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1915 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001916 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001917 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001918 }
1919 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1920 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001921 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001922 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001923 }
1924
1925 /* The second leg starts transmitting a little later */
1926 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1927 f_sleep(1.0);
1928 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1929 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001930 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001931 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001932 }
1933 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1934 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001935 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001936 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001937 }
1938
1939 /* The first leg will now be switched into bidirectional
1940 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001941 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1942 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1943 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001944 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1945 f_sleep(0.5);
1946 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001947 if (stats[0].num_pkts_rx_err_disabled != 0) {
1948 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001949 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001950 }
1951 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1952 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001953 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001954 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001955 }
1956
1957 /* When the second leg is switched into bidirectional mode
1958 * as well, then the MGW will connect the two together and
1959 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02001960 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1961 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001962 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001963 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1964 f_sleep(2.0);
1965
1966 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1967 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1968
1969 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].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 }
1974
1975 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1976 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001977 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001978 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001979 }
Philipp Maier36291392018-07-25 09:40:44 +02001980
1981 f_rtpem_stats_err_check(stats[0]);
1982 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001983
1984 /* Tear down */
1985 f_flow_delete(RTPEM[0]);
1986 f_flow_delete(RTPEM[1], ep, call_id);
1987 setverdict(pass);
1988 }
1989
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001990 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1991 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1992 mp_local_ipv4, mp_remote_ipv4);
1993 }
1994
1995 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
1996 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
1997 mp_local_ipv6, mp_remote_ipv6);
1998 }
1999
2000 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
2001 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2002 mp_local_ipv6, mp_remote_ipv6);
2003 }
2004
Philipp Maier2321ef92018-06-27 17:52:04 +02002005 /* Test what happens when two RTP streams from different sources target
2006 * a single connection. Is the unsolicited stream properly ignored? */
2007 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2008 var RtpFlowData flow[2];
2009 var RtpemStats stats[2];
2010 var MgcpResponse resp;
2011 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2012 var MgcpCallId call_id := '1234321326'H;
2013 var integer unsolicited_port := 10002;
2014
2015 f_init(ep);
2016
2017 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002018 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002019 /* bind local RTP emulation sockets */
2020 flow[0].em.portnr := 10000;
2021 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2022
2023 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002024 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002025 flow[1].em.portnr := 20000;
2026 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002027
2028 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2029 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2030
Philipp Maier2321ef92018-06-27 17:52:04 +02002031 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002032
Philipp Maier2321ef92018-06-27 17:52:04 +02002033 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002034 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2035 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002036 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2037
2038 f_sleep(0.5);
2039
Daniel Willmanna069d382018-12-13 13:53:33 +01002040 /* Stop transmitting packets and tear down the flows */
2041 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002042 f_flow_delete(RTPEM[0]);
2043 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002044
2045 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2046 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2047 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2048 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002049 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002050 }
2051
Philipp Maier36291392018-07-25 09:40:44 +02002052 f_rtpem_stats_err_check(stats[0]);
2053 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002054
Harald Weltebb7523b2018-03-29 08:52:01 +02002055 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002056 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002057
Philipp Maier2321ef92018-06-27 17:52:04 +02002058 /* Test a handover situation. We first create two connections transmit
2059 * some data bidirectionally. Then we will simulate a handover situation. */
2060 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2061 var RtpFlowData flow[2];
2062 var RtpemStats stats[3];
2063 var MgcpResponse resp;
2064 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2065 var MgcpCallId call_id := '76338'H;
2066 var integer port_old;
2067
2068 f_init(ep);
2069
2070 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002071 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002072 /* bind local RTP emulation sockets */
2073 flow[0].em.portnr := 10000;
2074 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2075
2076 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002077 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002078 flow[1].em.portnr := 20000;
2079 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2080
2081 /* Normal rtp flow for one second */
2082 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2083 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2084 f_sleep(1.0);
2085
2086 /* Now switch the flow over to a new port (BTS) */
2087 port_old := flow[0].em.portnr;
2088 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002089 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002090 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002091 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002092
2093 /* When handing over a call, the old source may still keep
2094 * transmitting for a while. We simulate this by injecting
2095 * some unsolicited packets on the behalf of the old source,
2096 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002097 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2098 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002099 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2100 f_sleep(1.0);
2101 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2102 f_sleep(1.0);
2103
2104 /* Terminate call */
2105 f_flow_delete(RTPEM[0]);
2106 f_flow_delete(RTPEM[1], ep, call_id);
2107
2108 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2109 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2110 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2111 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002112 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002113 }
2114 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2115 if (stats[2].num_pkts_rx_err_disabled != 0) {
2116 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002117 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002118 }
2119
Philipp Maier36291392018-07-25 09:40:44 +02002120 f_rtpem_stats_err_check(stats[0]);
2121 f_rtpem_stats_err_check(stats[1]);
2122 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002123
Philipp Maier2321ef92018-06-27 17:52:04 +02002124 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002125 }
Harald Weltef53f1642017-11-18 19:57:11 +01002126
Philipp Maier6d4e0942019-02-21 17:35:01 +01002127
2128 /* create two local RTP emulations; create two connections on MGW EP, see if
2129 * exchanged data is converted bwtween ts101318 and rfc5993 */
2130 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2131 var RtpFlowData flow[2];
2132 var RtpemStats stats[2];
2133 var MgcpResponse resp;
2134 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2135 var MgcpCallId call_id := '1226'H;
2136
2137 f_init(ep);
2138
2139 /* Turn on conversion mode */
2140 f_vty_enter_config(MGWVTY);
2141 f_vty_transceive(MGWVTY, "mgcp");
2142 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2143
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002144 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002145 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002146 /* bind local RTP emulation sockets */
2147 flow[0].em.portnr := 10000;
2148 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2149 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2150 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2151 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2152 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2153
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002154 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002155 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002156 flow[1].em.portnr := 20000;
2157 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2158 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2159 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2160 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2161 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2162
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002163 /* Send RTP packets to connection #0, receive on connection #1 */
2164 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2165 f_sleep(0.5);
2166 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002167 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002168 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2169 f_sleep(0.5);
2170 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002171
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002172 /* Send RTP packets to connection #1, receive on connection #0 */
2173 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2174 f_sleep(0.5);
2175 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2176 f_sleep(1.0);
2177 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2178 f_sleep(0.5);
2179 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2180
2181 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002182 f_flow_delete(RTPEM[0]);
2183 f_flow_delete(RTPEM[1], ep, call_id);
2184
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002185 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002186 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2187 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002188 f_rtpem_stats_err_check(stats[0]);
2189 f_rtpem_stats_err_check(stats[1]);
2190
2191 /* Turn off conversion mode */
2192 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2193
2194 setverdict(pass);
2195 }
2196
Philipp Maier4f764ce2019-03-07 10:54:10 +01002197 /* create two local RTP emulations; create two connections on MGW EP, see if
2198 * exchanged data is converted between AMR octet-aligned and bandwith
2199 * efficient-mode */
2200 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2201 var RtpFlowData flow[2];
2202 var RtpemStats stats[2];
2203 var MgcpResponse resp;
2204 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2205 var MgcpCallId call_id := '1226'H;
2206
2207 f_init(ep);
2208
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002209 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002210 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002211 /* bind local RTP emulation sockets */
2212 flow[0].em.portnr := 10000;
2213 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2214 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2215 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2216 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2217 flow[0].fmtp := fmtp0;
2218 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2219
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002220 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002221 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002222 flow[1].em.portnr := 20000;
2223 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2224 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2225 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2226 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2227 flow[1].fmtp := fmtp1;
2228 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2229
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002230 /* Send RTP packets to connection #0, receive on connection #1 */
2231 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2232 f_sleep(0.5);
2233 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002234 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002235 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2236 f_sleep(0.5);
2237 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002238
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002239 /* Send RTP packets to connection #1, receive on connection #0 */
2240 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2241 f_sleep(0.5);
2242 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2243 f_sleep(1.0);
2244 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2245 f_sleep(0.5);
2246 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2247
2248 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002249 f_flow_delete(RTPEM[0]);
2250 f_flow_delete(RTPEM[1], ep, call_id);
2251
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002252 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002253 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2254 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002255 f_rtpem_stats_err_check(stats[0]);
2256 f_rtpem_stats_err_check(stats[1]);
2257
2258 setverdict(pass);
2259 }
2260
Philipp Maier882843d2020-05-25 15:33:13 +02002261 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2262 * functions are real world AMR RTP payloads including AMR header. The
2263 * payloads were extracted from a trace with known good payloads. */
2264
Philipp Maier4f764ce2019-03-07 10:54:10 +01002265 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002266 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002267 }
2268
2269 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2270 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2271 }
2272
2273 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2274 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2275 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002276
Harald Weltee636afd2017-09-17 16:24:09 +08002277 /* TODO: Double-DLCX (no retransmission) */
2278
2279
2280
2281 /* TODO: AUEP (various) */
2282 /* TODO: RSIP (various) */
2283 /* TODO: RQNT (various) */
2284 /* TODO: EPCF (various) */
2285 /* TODO: AUCX (various) */
2286 /* TODO: invalid verb (various) */
2287
Oliver Smith021141e2019-06-25 12:09:01 +02002288
2289 testcase TC_conn_timeout() runs on dummy_CT {
2290 var RtpFlowData flow;
2291 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2292 var MgcpCallId call_id := '1225'H;
2293 var MGCP_RecvFrom mrf;
2294
2295 f_init(ep);
2296 log("Setting conn-timeout to 1s");
2297 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2298
2299 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002300 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002301 flow.em.portnr := 10000;
2302 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2303 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2304 f_sleep(1.5);
2305
2306 log("Stopping for 0.5s and resuming");
2307 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2308 f_sleep(0.5);
2309 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2310 f_sleep(0.1);
2311
2312 log("Stopping for 1.5s, expecting to run into timeout");
2313 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2314 f_sleep(1.5);
2315
2316 log("Resuming should fail now");
2317 f_rtpem_conn_refuse_expect(RTPEM[0]);
2318 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2319 f_sleep(0.2);
2320 f_rtpem_conn_refuse_verify(RTPEM[0]);
2321
2322 setverdict(pass);
2323 }
2324
Philipp Maier2609c752020-07-08 12:38:09 +02002325 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2326 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2327 var template MgcpCommand cmd;
2328 var MgcpResponse resp;
2329 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2330 var MgcpCallId call_id := '8376F297'H;
2331
2332 f_init(ep);
2333
2334 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2335 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2336
2337 f_dlcx_ok(ep);
2338
2339 setverdict(pass);
2340 }
2341
2342 /* Test what happens when overlapping endpoints are selected (E1) */
2343 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2344 var template MgcpCommand cmd;
2345 var MgcpResponse resp;
2346 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2347 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2348 var MgcpCallId call_id_1 := '8376F297'H;
2349 var MgcpCallId call_id_2 := '837AF2A7'H;
2350
2351 f_init();
2352
2353 /* ep_1 and ep_2 are overlapping, selecting both one after
2354 * another should work fine: */
2355 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2356 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2357 f_dlcx_ok(ep_1);
2358 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2359 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2360 f_dlcx_ok(ep_2);
2361
2362 /* When ep_1 is serving a call we can not select ep_2 becaus
2363 * it is overlapping with ep_1 */
2364 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2365 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2366 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2367 resp := mgcp_transceive_mgw(cmd, ?);
2368 if (resp.line.code != "501") {
2369 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2370 }
2371 f_dlcx_ok(ep_1);
2372
2373 setverdict(pass);
2374 }
2375
2376 /* Create one connection in loopback mode, test if the RTP packets are
2377 * actually reflected */
2378 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2379 var RtpFlowData flow;
2380 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2381 var MgcpCallId call_id := '12250989'H;
2382 var RtpemStats stats;
2383
2384 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002385 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002386 flow.em.portnr := 10000;
2387 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2388
2389 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2390 f_sleep(1.0);
2391 f_flow_delete(RTPEM[0], ep, call_id);
2392
2393 stats := f_rtpem_stats_get(RTPEM[0]);
2394
2395 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2396 setverdict(fail);
2397 }
2398 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2399 setverdict(fail);
2400 }
2401
2402 f_rtpem_stats_err_check(stats);
2403
2404 setverdict(pass);
2405 }
2406
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002407 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2408 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2409 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2410 var template MgcpCommand cmd;
2411 var MgcpResponse resp;
2412 var MgcpCallId call_id := '1234'H;
2413 var MgcpConnectionId conn_id;
2414
2415 f_init(ep);
2416
2417 /* create the connection on the MGW */
2418 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2419 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2420 conn_id := extract_conn_id(resp);
2421
2422 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2423 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2424 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2425 valueof(ts_SDP_ptime(20)) });
2426 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2427
2428 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2429 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2430 }
2431 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2432 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2433 }
2434
2435 /* clean-up */
2436 f_dlcx_ok(ep, call_id);
2437 setverdict(pass);
2438 }
2439
2440 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2441 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2442 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2443 var template MgcpCommand cmd;
2444 var MgcpResponse resp;
2445 var MgcpCallId call_id := '1234'H;
2446 var MgcpConnectionId conn_id;
2447
2448 f_init(ep);
2449
2450 /* create the connection on the MGW */
2451 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2452 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2453 conn_id := extract_conn_id(resp);
2454
2455 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2456 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2457 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2458 valueof(ts_SDP_ptime(20)) });
2459 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2460
2461 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2462 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2463 }
2464 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2465 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2466 }
2467
2468 /* clean-up */
2469 f_dlcx_ok(ep, call_id);
2470 setverdict(pass);
2471 }
2472
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002473 /* create two local RTP+IuUP emulations and pass data in both directions */
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002474 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002475 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002476 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2477 var RtpFlowData flow[2];
2478 var RtpemStats stats[2];
2479 var MgcpResponse resp;
2480 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2481 var MgcpCallId call_id := '1227'H;
2482 var integer num_pkts_tx[2];
2483 var integer temp;
2484
2485 f_init(ep);
2486
2487 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2488 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2489 flow[0].em.portnr := 10000;
2490 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2491 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2492 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002493 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002494 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002495 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2496 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2497
2498 /* Create the second connection. This connection will be also
2499 * in receive only mode (CN side, IuUP-Init passive) */
2500 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2501 flow[1].em.portnr := 20000;
2502 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2503 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2504 flow[1].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002505 flow[1].rtp_cfg.iuup_cfg.active_init := false;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002506 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2507 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2508
2509 /* The first leg starts transmitting */
2510 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2511 f_sleep(0.5);
2512 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2513 if (stats[0].num_pkts_rx_err_disabled != 0) {
2514 setverdict(fail, "received packets from MGW on recvonly connection 0");
2515 mtc.stop;
2516 }
2517 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2518 if (stats[1].num_pkts_rx_err_disabled != 0) {
2519 setverdict(fail, "received packets from MGW on recvonly connection 1");
2520 mtc.stop;
2521 }
2522
2523 /* The second leg starts transmitting a little later */
2524 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2525 f_sleep(1.0);
2526 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2527 if (stats[0].num_pkts_rx_err_disabled != 0) {
2528 setverdict(fail, "received packets from MGW on recvonly connection 0");
2529 mtc.stop;
2530 }
2531 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2532 if (stats[1].num_pkts_rx_err_disabled != 0) {
2533 setverdict(fail, "received packets from MGW on recvonly connection 1");
2534 mtc.stop;
2535 }
2536
2537 /* The first leg will now be switched into bidirectional
2538 * mode, but we do not expect any data coming back yet. */
2539 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2540 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2541 num_pkts_tx[1] := stats[1].num_pkts_tx;
2542 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2543 f_sleep(0.5);
2544 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2545 if (stats[0].num_pkts_rx_err_disabled != 0) {
2546 setverdict(fail, "received packets from MGW on recvonly connection 0");
2547 mtc.stop;
2548 }
2549 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2550 if (stats[1].num_pkts_rx_err_disabled != 0) {
2551 setverdict(fail, "received packets from MGW on recvonly connection 1");
2552 mtc.stop;
2553 }
2554
2555 /* When the second leg is switched into bidirectional mode
2556 * as well, then the MGW will connect the two together and
2557 * we should see RTP streams passing through from both ends. */
2558 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2559 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2560 num_pkts_tx[0] := stats[0].num_pkts_tx;
2561 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2562 f_sleep(2.0);
2563
2564 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2565 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2566
2567 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].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 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2574 if (temp > 3 or temp < -3) {
2575 setverdict(fail, "number of packets not within normal parameters:", temp);
2576 mtc.stop;
2577 }
2578
2579 f_rtpem_stats_err_check(stats[0]);
2580 f_rtpem_stats_err_check(stats[1]);
2581
2582 /* Tear down */
2583 f_flow_delete(RTPEM[0]);
2584 f_flow_delete(RTPEM[1], ep, call_id);
2585 setverdict(pass);
2586 }
2587 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002588 var template (value) IuUP_RabFlowCombinationList rfcl := {
2589 t_IuUP_RFC_AMR_12_2(0),
2590 t_IuUP_RFC_AMR_SID(1),
2591 t_IuUP_RFC_AMR_NO_DATA(2)
2592 };
2593 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2594 mp_local_ipv4, mp_remote_ipv4);
2595 }
2596 /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */
2597 testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT {
2598 var template (value) IuUP_RabFlowCombinationList rfcl := {
2599 t_IuUP_RFC_AMR_12_2(1),
2600 t_IuUP_RFC_AMR_SID(2),
2601 t_IuUP_RFC_AMR_NO_DATA(0)
2602 };
2603 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002604 mp_local_ipv4, mp_remote_ipv4);
2605 }
2606
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002607 /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */
2608 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 +02002609 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002610 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2611 var RtpFlowData flow[2];
2612 var RtpemStats stats[2];
2613 var MgcpResponse resp;
2614 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2615 var MgcpCallId call_id := '1227'H;
2616 var integer num_pkts_tx[2];
2617 var integer temp;
2618
2619 f_init(ep);
2620
2621 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2622 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2623 flow[0].em.portnr := 10000;
2624 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2625 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2626 flow[0].rtp_cfg.tx_fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
2627 flow[0].rtp_cfg.rx_fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O; /* flow[1].rtp_cfg.tx_fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
2628 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002629 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002630 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002631 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2632 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2633
2634 /* Create the second connection. This connection will be also
2635 * in receive only mode (CN side, regular RTP) */
2636 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
2637 flow[1].em.portnr := 20000;
2638 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2639 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2640 flow[1].rtp_cfg.tx_fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
2641 flow[1].rtp_cfg.rx_fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O; /* flow[0].rtp_cfg.tx_fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
2642 flow[1].rtp_cfg.iuup_mode := false;
2643 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2644 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2645
2646 /* The first leg starts transmitting */
2647 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2648 f_sleep(0.5);
2649 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2650 if (stats[0].num_pkts_rx_err_disabled != 0) {
2651 setverdict(fail, "received packets from MGW on recvonly connection 0");
2652 mtc.stop;
2653 }
2654 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2655 if (stats[1].num_pkts_rx_err_disabled != 0) {
2656 setverdict(fail, "received packets from MGW on recvonly connection 1");
2657 mtc.stop;
2658 }
2659
2660 /* The second leg starts transmitting a little later */
2661 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2662 f_sleep(1.0);
2663 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2664 if (stats[0].num_pkts_rx_err_disabled != 0) {
2665 setverdict(fail, "received packets from MGW on recvonly connection 0");
2666 mtc.stop;
2667 }
2668 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2669 if (stats[1].num_pkts_rx_err_disabled != 0) {
2670 setverdict(fail, "received packets from MGW on recvonly connection 1");
2671 mtc.stop;
2672 }
2673
2674 /* The first leg will now be switched into bidirectional
2675 * mode, but we do not expect any data coming back yet. */
2676 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2677 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2678 num_pkts_tx[1] := stats[1].num_pkts_tx;
2679 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2680 f_sleep(0.5);
2681 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2682 if (stats[0].num_pkts_rx_err_disabled != 0) {
2683 setverdict(fail, "received packets from MGW on recvonly connection 0");
2684 mtc.stop;
2685 }
2686 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2687 if (stats[1].num_pkts_rx_err_disabled != 0) {
2688 setverdict(fail, "received packets from MGW on recvonly connection 1");
2689 mtc.stop;
2690 }
2691
2692 /* When the second leg is switched into bidirectional mode
2693 * as well, then the MGW will connect the two together and
2694 * we should see RTP streams passing through from both ends. */
2695 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2696 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2697 num_pkts_tx[0] := stats[0].num_pkts_tx;
2698 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2699 f_sleep(2.0);
2700
2701 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2702 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2703
2704 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].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 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2711 if (temp > 3 or temp < -3) {
2712 setverdict(fail, "number of packets not within normal parameters:", temp);
2713 mtc.stop;
2714 }
2715
2716 f_rtpem_stats_err_check(stats[0]);
2717 f_rtpem_stats_err_check(stats[1]);
2718
2719 /* Tear down */
2720 f_flow_delete(RTPEM[0]);
2721 f_flow_delete(RTPEM[1], ep, call_id);
2722 setverdict(pass);
2723 }
2724 testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002725 var template (value) IuUP_RabFlowCombinationList rfcl := {
2726 t_IuUP_RFC_AMR_12_2(0),
2727 t_IuUP_RFC_AMR_SID(1),
2728 t_IuUP_RFC_AMR_NO_DATA(2)
2729 };
2730 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2731 mp_local_ipv4, mp_remote_ipv4);
2732 }
2733 /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */
2734 testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT {
2735 var template (value) IuUP_RabFlowCombinationList rfcl := {
2736 t_IuUP_RFC_AMR_12_2(1),
2737 t_IuUP_RFC_AMR_SID(2),
2738 t_IuUP_RFC_AMR_NO_DATA(0)
2739 };
2740 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002741 mp_local_ipv4, mp_remote_ipv4);
2742 }
2743
Harald Welte00a067f2017-09-13 23:27:17 +02002744 control {
2745 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002746 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002747 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002748 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002749 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002750 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002751 execute(TC_crcx_early_bidir_mode());
2752 execute(TC_crcx_unsupp_param());
2753 execute(TC_crcx_missing_callid());
2754 execute(TC_crcx_missing_mode());
2755 execute(TC_crcx_unsupp_packet_intv());
2756 execute(TC_crcx_illegal_double_lco());
2757 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002758 execute(TC_crcx_wildcarded());
2759 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002760 execute(TC_mdcx_without_crcx());
2761 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02002762 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002763 execute(TC_mdcx_wildcarded());
2764 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002765 execute(TC_crcx_and_dlcx_ep_callid_connid());
2766 execute(TC_crcx_and_dlcx_ep_callid());
2767 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002768 execute(TC_crcx_and_dlcx_ep_callid_inval());
2769 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002770 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002771
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002772 execute(TC_crcx_osmux_wildcard());
2773 execute(TC_crcx_osmux_fixed());
2774 execute(TC_crcx_osmux_fixed_twice());
2775 execute(TC_one_crcx_receive_only_osmux());
2776 execute(TC_one_crcx_loopback_osmux());
2777 execute(TC_two_crcx_and_rtp_osmux());
2778 execute(TC_two_crcx_and_rtp_osmux_bidir());
2779 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2780 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2781
Harald Welte33d82162017-12-28 03:21:57 +01002782 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002783
2784 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002785
2786 execute(TC_one_crcx_receive_only_rtp());
2787 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002788 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02002789 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002790 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002791 execute(TC_two_crcx_diff_pt_and_rtp());
2792 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002793 execute(TC_two_crcx_mdcx_and_rtp());
2794 execute(TC_two_crcx_and_unsolicited_rtp());
2795 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002796 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002797 execute(TC_amr_oa_bwe_rtp_conversion());
2798 execute(TC_amr_oa_oa_rtp_conversion());
2799 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002800
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002801 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002802
2803 execute(TC_e1_crcx_and_dlcx_ep());
2804 execute(TC_e1_crcx_with_overlap());
2805 execute(TC_e1_crcx_loopback());
2806
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002807 execute(TC_crcx_mdcx_ip4());
2808 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002809 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2810 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2811 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2812 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2813 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2814 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2815 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2816 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02002817
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002818 execute(TC_two_crcx_mdcx_and_iuup());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002819 execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered());
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002820 execute(TC_two_crcx_mdcx_and_iuup_rtp());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002821 execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered());
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002822
Philipp Maier37965082021-05-25 16:44:25 +02002823 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
2824 * older versions of osmo-mgw. This eventually leads into
2825 * a failure of all subsequent testcases, so it is important
2826 * not to add new testcaes after this one. */
2827 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02002828 }
2829}