blob: 5ff5247e9aae0d6eff44e600c09af26d0957603d [file] [log] [blame]
Harald Welte34b5a952019-05-27 11:54:11 +02001/* MGW (Media Gateway) test suite in TTCN-3
2 * (C) 2017-2018 Harald Welte <laforge@gnumonks.org>
3 * (C) 2018-2019 sysmocom - s.f.m.c. GmbH
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
Harald Welte00a067f2017-09-13 23:27:17 +020012module MGCP_Test {
Harald Weltef07c2862017-11-18 17:16:24 +010013 import from Osmocom_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +020014 import from MGCP_Types all;
Harald Welte4029e8c2017-11-23 22:00:42 +010015 import from MGCP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080016 import from SDP_Types all;
17 import from MGCP_CodecPort all;
18 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +010019 import from RTP_CodecPort all;
20 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020021 import from RTP_Emulation all;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +020022 import from IuUP_Emulation all;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020023 import from OSMUX_Types all;
24 import from OSMUX_CodecPort all;
25 import from OSMUX_CodecPort_CtrlFunct all;
26 import from OSMUX_Emulation all;
Pau Espin Pedrol71ed4632022-09-02 18:51:19 +020027 import from AMR_Types all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080028 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010029 import from General_Types all;
30 import from Native_Functions all;
31 import from IPCP_Types all;
32 import from IP_Types all;
33 import from Osmocom_VTY_Functions all;
34 import from TELNETasp_PortType all;
Philipp Maier55b90542021-07-02 12:33:19 +020035 import from StatsD_Types all;
36 import from StatsD_CodecPort all;
37 import from StatsD_CodecPort_CtrlFunct all;
38 import from StatsD_Checker all;
Philipp Maier3560bd62021-08-19 11:52:23 +020039 import from Osmocom_CTRL_Functions all;
40 import from Osmocom_CTRL_Types all;
41 import from Osmocom_CTRL_Adapter all;
Harald Welte00a067f2017-09-13 23:27:17 +020042
Philipp Maierbb7a01c2018-02-01 12:32:57 +010043 const charstring c_mgw_domain := "mgw";
44 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
45
Harald Welte21ba5572017-09-19 17:55:05 +080046 /* any variables declared in the component will be available to
47 * all functions that 'run on' the named component, similar to
48 * class members in C++ */
Philipp Maier3560bd62021-08-19 11:52:23 +020049 type component dummy_CT extends StatsD_ConnHdlr, CTRL_Adapter_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080050 port MGCP_CODEC_PT MGCP;
51 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010052 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080053 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010054
Philipp Maier2321ef92018-06-27 17:52:04 +020055 var RTP_Emulation_CT vc_RTPEM[3];
56 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010057
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020058 var OSMUX_Emulation_CT vc_OsmuxEM;
59 port OsmuxEM_CTRL_PT OsmuxEM;
60
Philipp Maier6137c322019-02-20 16:13:41 +010061 port TELNETasp_PT MGWVTY;
Philipp Maier55b90542021-07-02 12:33:19 +020062
63 var StatsD_Checker_CT vc_STATSD;
Harald Welte00a067f2017-09-13 23:27:17 +020064 };
65
Harald Weltee1e18c52017-09-17 16:23:07 +080066 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
67 var MgcpTransId tid := int2str(g_trans_id);
68 g_trans_id := g_trans_id + 1;
69 return tid;
70 }
71
Harald Welte21ba5572017-09-19 17:55:05 +080072 /* all parameters declared here can be modified / overridden by
73 * the config file in the [MODULE_PARAMETERS] section. If no
74 * config file is used or the file doesn't specify them, the
75 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080076 modulepar {
77 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020078 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020079 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080080 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020081 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020082 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010083 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020084 PortNumber mp_local_osmux_port := 1985;
Philipp Maier55b90542021-07-02 12:33:19 +020085 PortNumber mp_mgw_statsd_port := 8125;
Philipp Maier3560bd62021-08-19 11:52:23 +020086 PortNumber mp_mgw_ctrl_port := 4267;
Pau Espin Pedrole7928872022-10-07 10:57:11 +020087 /* Maximum number of available endpoints in osmo-mgw.cfg ("number endpoints"): */
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +020088 integer mp_num_endpoints := 300;
Harald Welte3c6ebb92017-09-16 00:56:57 +080089 }
90
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020091 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
92 /* Turn on conversion mode */
93 f_vty_enter_config(MGWVTY);
94 f_vty_transceive(MGWVTY, "mgcp");
95 if (osmux_on) {
96 f_vty_transceive(MGWVTY, "osmux on");
97 } else {
98 f_vty_transceive(MGWVTY, "osmux off");
99 }
100 f_vty_transceive(MGWVTY, "exit");
101 f_vty_transceive(MGWVTY, "exit");
102
103 }
104
105 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +0100106 map(self:MGWVTY, system:MGWVTY);
107 f_vty_set_prompts(MGWVTY);
108 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200109
110 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +0100111 }
112
Harald Weltebb7523b2018-03-29 08:52:01 +0200113 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
114 runs on dummy_CT {
115 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
116 map(comp_ref:RTP, system:RTP);
117 map(comp_ref:RTCP, system:RTCP);
118 comp_ref.start(RTP_Emulation.f_main());
119 }
120
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200121 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
122 runs on dummy_CT {
123 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
124 map(comp_ref:OSMUX, system:OSMUX);
125 comp_ref.start(OSMUX_Emulation.f_main());
126 }
127
Harald Welte21ba5572017-09-19 17:55:05 +0800128 /* initialization function, called by each test case at the
129 * beginning, but 'initialized' variable ensures its body is
130 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200131 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800132 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100133 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200134
Harald Welteedc45c12017-11-18 19:15:05 +0100135 if (initialized == false) {
136 initialized := true;
137
138 /* some random number for the initial transaction id */
139 g_trans_id := float2int(rnd()*65535.0);
140 map(self:MGCP, system:MGCP_CODEC_PT);
141 /* connect the MGCP test port using the given
142 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
143 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200144 res := MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, mp_remote_ipv4, mp_remote_udp_port, mp_local_ipv4, mp_local_udp_port, 0, { udp := {} });
Harald Welte9220f632018-05-23 20:27:02 +0200145 if (not ispresent(res.connId)) {
146 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200147 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200148 }
Harald Welteedc45c12017-11-18 19:15:05 +0100149 g_mgcp_conn_id := res.connId;
150
Harald Weltebb7523b2018-03-29 08:52:01 +0200151 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
152 f_rtpem_init(vc_RTPEM[i], i);
153 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
154 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200155 if (osmux_on) {
156 f_osmuxem_init(vc_OsmuxEM);
157 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
158 }
Philipp Maier55b90542021-07-02 12:33:19 +0200159
Philipp Maier2ff3e662021-08-19 10:52:33 +0200160 f_init_statsd("VirtCallAgent", vc_STATSD, mp_local_ipv4, mp_mgw_statsd_port);
Philipp Maier55b90542021-07-02 12:33:19 +0200161 connect(self:STATSD_PROC, vc_STATSD:STATSD_PROC);
162
163 f_statsd_reset();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800164 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800165
Philipp Maier3560bd62021-08-19 11:52:23 +0200166 f_ipa_ctrl_start_client(mp_remote_ipv4, mp_mgw_ctrl_port);
167
Harald Welteedc45c12017-11-18 19:15:05 +0100168 if (isvalue(ep)) {
169 /* do a DLCX on all connections of the EP */
170 f_dlcx_ignore(valueof(ep));
171 }
Philipp Maier6137c322019-02-20 16:13:41 +0100172
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200173 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800174 }
175
Harald Welte00a067f2017-09-13 23:27:17 +0200176 testcase TC_selftest() runs on dummy_CT {
177 const charstring c_auep := "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100178 const charstring c_mdcx3 := "MDCX 18983215 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n";
Harald Welte00a067f2017-09-13 23:27:17 +0200179 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
180 "I: 1\n" &
181 "\n" &
182 "v=0\r\n" &
183 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
184 "s=-\r\n" &
185 "c=IN IP4 0.0.0.0\r\n" &
186 "t=0 0\r\n" &
187 "m=audio 0 RTP/AVP 126\r\n" &
188 "a=rtpmap:126 AMR/8000\r\n" &
189 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100190 const charstring c_mdcx4 := "MDCX 18983216 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200191 "M: sendrecv\r" &
192 "C: 2\r\n" &
193 "I: 1\r\n" &
194 "L: p:20, a:AMR, nt:IN\r\n" &
195 "\n" &
196 "v=0\r\n" &
197 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800198 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200199 "c=IN IP4 0.0.0.0\r\n" &
200 "t=0 0\r\n" &
201 "m=audio 4441 RTP/AVP 99\r\n" &
202 "a=rtpmap:99 AMR/8000\r\n" &
203 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800204 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200205
206 log(c_auep);
207 log(dec_MgcpCommand(c_auep));
208
209 log(c_mdcx3);
210 log(dec_MgcpCommand(c_mdcx3));
211
212 log(c_mdcx3_ret);
213 log(dec_MgcpResponse(c_mdcx3_ret));
214
215 log(c_mdcx4);
216 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800217
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100218 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
219 log(enc_MgcpCommand(valueof(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H))));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800220
221 log(c_crcx510_ret);
222 log(dec_MgcpResponse(c_crcx510_ret));
223 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100224
225 /* We didn't encounter any DTE, so pass the test */
226 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800227 }
228
Harald Weltee636afd2017-09-17 16:24:09 +0800229 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100230 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800231 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
232 * - CRCX with remote session description and without
233 *
234 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100235 * x packetization != 20ms
236 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800237 * x unsupported mode (517)
238 * x bidirectional mode before RemoteConnDesc: 527
239 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100240 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800241 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
242 */
243
Harald Welte21ba5572017-09-19 17:55:05 +0800244 /* build a receive template for receiving a MGCP message. You
245 * pass the MGCP response template in, and it will generate an
246 * MGCP_RecvFrom template that can match the primitives arriving on the
247 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800248 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
249 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100250 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200251 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800252 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200253 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800254 locPort := mp_local_udp_port,
255 msg := { response := resp }
256 }
257 return mrf;
258 }
259
260 /* Send a MGCP request + receive a (matching!) response */
261 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
262 var MgcpMessage msg := { command := valueof(cmd) };
263 resp.line.trans_id := cmd.line.trans_id;
264 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800265 var MGCP_RecvFrom mrf;
266 timer T := 5.0;
267
Harald Welte55015362017-11-18 16:02:42 +0100268 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800269 T.start;
270 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800271 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200272 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
273 setverdict(fail, "Response didn't match template");
274 mtc.stop;
275 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800276 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200277 [] T.timeout {
278 setverdict(fail, "Timeout waiting for response to ", cmd);
279 mtc.stop;
280 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800281 }
282 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800283
284 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
285 return mrf.msg.response;
286 } else {
287 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
288 return r;
289 }
Harald Welte00a067f2017-09-13 23:27:17 +0200290 }
291
Harald Welteba62c8c2017-11-18 18:26:49 +0100292 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
293 var integer i;
294 for (i := 0; i < lengthof(resp.params); i := i + 1) {
295 var MgcpParameter par := resp.params[i];
296 if (par.code == "I") {
297 return str2hex(par.val);
298 }
299 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200300 setverdict(fail, "Could not find conn id for MgcpReponse");
301 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100302 return '00000000'H;
303 }
304
Harald Welte10889c12017-11-18 19:40:31 +0100305 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
306 template MgcpCallId call_id := omit,
307 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100308 var template MgcpCommand cmd;
309 var MgcpResponse resp;
310 var template MgcpResponse rtmpl := {
311 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100312 code := ret_code,
313 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100314 },
315 params := *,
316 sdp := *
317 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100318 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100319 resp := mgcp_transceive_mgw(cmd, rtmpl);
320 }
321
Harald Welte10889c12017-11-18 19:40:31 +0100322 /* Send DLCX and expect OK response */
323 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
324 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100325 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100326 }
327
Harald Welteba62c8c2017-11-18 18:26:49 +0100328 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100329 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100330 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100331 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100332 }
333
Harald Weltebb7523b2018-03-29 08:52:01 +0200334 type record HostPort {
335 charstring hostname,
336 integer portnr optional
337 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200338 type record RtpOsmuxFlowData {
339 boolean local_cid_sent, /* whther non wildcarded CID was already sent to MGW */
340 MgcpOsmuxCID local_cid optional,
341 MgcpOsmuxCID remote_cid optional,
342 OsmuxemConfig cfg optional
343 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200344 type record RtpFlowData {
345 HostPort em, /* emulation side */
346 HostPort mgw, /* mgw side */
347 uint7_t pt,
348 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200349 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100350 RtpemConfig rtp_cfg optional,
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200351 RtpOsmuxFlowData osmux,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100352 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200353 }
354
Philipp Maier2321ef92018-06-27 17:52:04 +0200355 /* Create an RTP flow (bidirectional, or receive-only) */
356 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 +0200357 boolean one_phase := true)
358 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200359 var template MgcpCommand cmd;
360 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100361 var SDP_attribute_list attributes;
362
363 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
364 if (isvalue(flow.fmtp)) {
365 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
366 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200367
368 /* bind local RTP emulation socket */
369 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
370
Philipp Maier28bb8292018-07-20 17:09:17 +0200371 /* configure rtp-emulation */
372 if (ispresent(flow.rtp_cfg)) {
373 f_rtpem_configure(pt, flow.rtp_cfg);
374 } else {
375 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
376 rtp_cfg.tx_payload_type := flow.pt
377 f_rtpem_configure(pt, rtp_cfg);
378 }
379
Harald Weltebb7523b2018-03-29 08:52:01 +0200380 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200381 /* Connect flow to MGW using a CRCX that also contains an SDP
382 * part that tells the MGW where we are listening for RTP streams
383 * that come from the MGW. We get a fully working connection in
384 * one go. */
385
386 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200387 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100388 flow.em.portnr, { int2str(flow.pt) }, attributes);
389
Harald Weltebb7523b2018-03-29 08:52:01 +0200390 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
391 flow.mgcp_conn_id := extract_conn_id(resp);
392 /* extract port number from response */
393 flow.mgw.portnr :=
394 resp.sdp.media_list[0].media_field.ports.port_number;
395 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200396 /* Create a half-open connection only. We do not tell the MGW
397 * where it can send RTP streams to us. This means this
398 * connection will only be able to receive but can not send
399 * data back to us. In order to turn the connection in a fully
400 * bi-directional one, a separate MDCX is needed. */
401
402 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200403 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
404 flow.mgcp_conn_id := extract_conn_id(resp);
405 /* extract MGW-side port number from response */
406 flow.mgw.portnr :=
407 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200408 }
409 /* finally, connect the emulation-side RTP socket to the MGW */
410 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
411 }
412
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200413 /* Create an Osmux flow (bidirectional, or receive-only) */
414 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
415 boolean one_phase := true)
416 runs on dummy_CT {
417 var template MgcpCommand cmd;
418 var MgcpResponse resp;
419 var SDP_attribute_list attributes;
420 var OsmuxTxHandle tx_hdl;
421 var OsmuxRxHandle rx_hdl;
422 var charstring cid_response;
423 var OsmuxCID cid_resp_parsed
424
425 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
426 if (isvalue(flow.fmtp)) {
427 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
428 }
429
430 /* bind local Osmux emulation socket */
431 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
432
433 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200434 if (ispresent(flow.osmux.cfg)) {
435 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200436 } else {
437 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
438 f_osmuxem_configure(pt, osmux_cfg);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200439 flow.osmux.cfg := osmux_cfg
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200440 }
441
442 if (one_phase) {
443 /* Connect flow to MGW using a CRCX that also contains an SDP
444 * part that tells the MGW where we are listening for Osmux streams
445 * that come from the MGW. We get a fully working connection in
446 * one go. */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200447 if (flow.osmux.local_cid != -1) {
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200448 /* We may still want to negotiate osmux CID later at MDCX */
449 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200450 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200451 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200452 flow.osmux.local_cid_sent := true;
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200453 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200454 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200455 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
456 flow.em.portnr, { int2str(flow.pt) }, attributes);
457 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
458 flow.mgcp_conn_id := extract_conn_id(resp);
459 /* extract port number from response */
460 flow.mgw.portnr :=
461 resp.sdp.media_list[0].media_field.ports.port_number;
462 } else {
463 /* Create a half-open connection only. We do not tell the MGW
464 * where it can send Osmux streams to us. This means this
465 * connection will only be able to receive but can not send
466 * data back to us. In order to turn the connection in a fully
467 * bi-directional one, a separate MDCX is needed. */
468
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200469 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200470 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
471
472 flow.mgcp_conn_id := extract_conn_id(resp);
473 /* extract MGW-side port number from response */
474 flow.mgw.portnr :=
475 resp.sdp.media_list[0].media_field.ports.port_number;
476 }
477
478 /* extract Osmux CID we got assigned by the MGW */
479 var MgcpMessage resp_msg := {
480 response := resp
481 }
482
483 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
484 setverdict(fail, "No Osmux CID in MGCP response", resp);
485 mtc.stop;
486 }
487
488 /* Make sure response is no wildcard */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200489 flow.osmux.remote_cid := f_mgcp_osmux_cid_decode(cid_response);
490 if (flow.osmux.remote_cid == -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200491 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
492 mtc.stop;
493 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200494 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux.remote_cid));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200495 f_osmuxem_register_txhandle(pt, tx_hdl);
496
497 /* finally, connect the emulation-side RTP socket to the MGW */
498 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
499 }
500
Philipp Maier2321ef92018-06-27 17:52:04 +0200501 /* Modify an existing RTP flow */
502 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
503 runs on dummy_CT {
504 var template MgcpCommand cmd;
505 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100506 var SDP_attribute_list attributes;
507
508 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
509 if (isvalue(flow.fmtp)) {
510 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
511 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200512
513 /* rebind local RTP emulation socket to the new address */
514 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
515
Philipp Maier28bb8292018-07-20 17:09:17 +0200516 /* reconfigure rtp-emulation */
517 if (ispresent(flow.rtp_cfg)) {
518 f_rtpem_configure(pt, flow.rtp_cfg);
519 } else {
520 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
521 rtp_cfg.tx_payload_type := flow.pt
522 f_rtpem_configure(pt, rtp_cfg);
523 }
524
Philipp Maier2321ef92018-06-27 17:52:04 +0200525 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
526 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
527 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100528 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200529 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
530
531 /* extract MGW-side port number from response. (usually this
532 * will not change, but thats is up to the MGW) */
533 flow.mgw.portnr :=
534 resp.sdp.media_list[0].media_field.ports.port_number;
535
536 /* reconnect the emulation-side RTP socket to the MGW */
537 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
538 }
539
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200540 /* Modify an existing Osmux flow */
541 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
542 runs on dummy_CT {
543 var template MgcpCommand cmd;
544 var MgcpResponse resp;
545 var SDP_attribute_list attributes;
546 var OsmuxRxHandle rx_hdl;
547 var charstring cid_response;
548 var OsmuxCID cid_resp_parsed
549
550 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
551 if (isvalue(flow.fmtp)) {
552 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
553 }
554
555 /* rebind local Osmux emulation socket to the new address */
556 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
557
558 /* configure osmux-emulation */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200559 if (ispresent(flow.osmux.cfg)) {
560 f_osmuxem_configure(pt, flow.osmux.cfg);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200561 } else {
562 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
563 f_osmuxem_configure(pt, osmux_cfg);
564 }
565
566 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200567 if (flow.osmux.local_cid_sent == false and flow.osmux.local_cid != -1) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200568 rx_hdl := c_OsmuxemDefaultRxHandle;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200569 rx_hdl.cid := flow.osmux.local_cid;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200570 f_osmuxem_register_rxhandle(pt, rx_hdl);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200571 flow.osmux.local_cid_sent := true;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200572 }
573
574 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200575 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux.local_cid);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200576 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
577 flow.em.portnr, { int2str(flow.pt) }, attributes);
578 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
579
580 /* extract MGW-side port number from response. (usually this
581 * will not change, but thats is up to the MGW) */
582 flow.mgw.portnr :=
583 resp.sdp.media_list[0].media_field.ports.port_number;
584
585 /* extract Osmux CID we got assigned by the MGW */
586 var MgcpMessage resp_msg := {
587 response := resp
588 }
589
590 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
591 setverdict(fail, "No Osmux CID in MGCP response", resp);
592 mtc.stop;
593 }
594
595 /* Make sure response is no wildcard */
596 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
597 if (cid_resp_parsed == -1) {
598 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
599 mtc.stop;
600 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +0200601 if (cid_resp_parsed != flow.osmux.remote_cid) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200602 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
603 mtc.stop;
604 }
605
606 /* reconnect the emulation-side Osmux socket to the MGW */
607 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
608 }
609
Philipp Maier2321ef92018-06-27 17:52:04 +0200610 /* Delete an existing RTP flow */
611 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
612 runs on dummy_CT {
613 var template MgcpCommand cmd;
614 var MgcpResponse resp;
615
616 /* Switch off RTP flow */
617 f_rtpem_mode(pt, RTPEM_MODE_NONE);
618
619 /* Delete connection on MGW (if needed) */
620 if (isvalue(call_id) and isvalue(ep)) {
621 f_sleep(0.1);
622 f_dlcx_ok(valueof(ep), call_id);
623 }
624 }
625
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200626 /* Delete an existing Osmux flow */
627 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
628 runs on dummy_CT {
629 var template MgcpCommand cmd;
630 var MgcpResponse resp;
631
632 /* Switch off Osmux flow */
633 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
634
635 /* Delete connection on MGW (if needed) */
636 if (isvalue(call_id) and isvalue(ep)) {
637 f_sleep(0.1);
638 f_dlcx_ok(valueof(ep), call_id);
639 }
640 }
641
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100642 function f_crcx(charstring ep_prefix) runs on dummy_CT {
643 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800644 var template MgcpCommand cmd;
645 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100646 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800647
Harald Welteedc45c12017-11-18 19:15:05 +0100648 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800649
Harald Welteba62c8c2017-11-18 18:26:49 +0100650 /* create the connection on the MGW */
651 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100652 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100653 extract_conn_id(resp);
654
655 /* clean-up */
656 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100657 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100658
Philipp Maier45635f42018-06-05 17:28:02 +0200659 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
660 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
661 var template MgcpCommand cmd;
662 var MgcpResponse resp;
663 var MgcpCallId call_id := '1234'H;
664
665 f_init(ep);
666
667 /* create the connection on the MGW */
668 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
669 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
670 extract_conn_id(resp);
671
672 /* clean-up */
673 f_dlcx_ok(ep, call_id);
674
675 /* See also OS#2658: Even when we omit the LCO information, we
676 expect the MGW to pick a sane payload type for us. This
677 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200678 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200679 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200680 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200681 }
682
683 /* See also OS#2658: We also expect the MGW to assign a port
684 number to us. */
685 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
686 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200687 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200688 }
689 }
690
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200691 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
692 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
693 var template MgcpCommand cmd;
694 var MgcpResponse resp;
695 var MgcpCallId call_id := '1234'H;
696 var charstring cid_response;
697
698 if (run_init) {
699 f_init(ep, true);
700 }
701
702 /* create the connection on the MGW */
703 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
704 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
705 extract_conn_id(resp);
706
707 /* extract Osmux CID we got assigned by the MGW */
708 var MgcpMessage resp_msg := {
709 response := resp
710 }
711
712 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
713 setverdict(fail, "No Osmux CID in MGCP response", resp);
714 mtc.stop;
715 }
716
717 /* Make sure response is no wildcard */
718 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
719 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
720 mtc.stop;
721 }
722
723 /* clean-up */
724 f_dlcx_ok(ep, call_id);
725 }
726
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100727 /* test valid CRCX without SDP */
728 testcase TC_crcx() runs on dummy_CT {
729 f_crcx(c_mgw_ep_rtpbridge);
730 setverdict(pass);
731 }
732
Philipp Maier45635f42018-06-05 17:28:02 +0200733 /* test valid CRCX without SDP and LCO */
734 testcase TC_crcx_no_lco() runs on dummy_CT {
735 f_crcx_no_lco(c_mgw_ep_rtpbridge);
736 setverdict(pass);
737 }
738
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100739 /* test valid CRCX without SDP (older method without endpoint prefix) */
740 testcase TC_crcx_noprefix() runs on dummy_CT {
741 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800742 setverdict(pass);
743 }
744
745 /* test CRCX with unsupported mode, expect 517 */
746 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
747 var template MgcpCommand cmd;
748 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100749 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100750 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800751 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
752
Harald Welteedc45c12017-11-18 19:15:05 +0100753 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800754
Harald Welteba62c8c2017-11-18 18:26:49 +0100755 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800756 resp := mgcp_transceive_mgw(cmd, rtmpl);
757 setverdict(pass);
758 }
759
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200760 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
761 testcase TC_crcx_osmo_ign() runs on dummy_CT {
762 var template MgcpCommand cmd;
763 var MgcpResponse resp;
764 var MgcpEndpoint ep := "7@" & c_mgw_domain;
765 var MgcpCallId call_id := '3'H;
766
767 f_init(ep);
768
769 /* CRCX 1 7@mgw MGCP 1.0
770 C: 3
771 L: p:20, a:GSM-EFR, nt:IN
772 M: recvonly
773 X-Osmo-IGN: C
774 */
775
776 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
777 cmd.params := {ts_MgcpParCallId(call_id),
778 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
779 t_MgcpParConnMode("recvonly"),
780 t_MgcpParOsmoIGN("C")};
781 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
782 extract_conn_id(resp);
783
784 /* clean-up */
785 f_dlcx_ok(ep, call_id);
786 setverdict(pass);
787 }
788
Harald Welte21ba5572017-09-19 17:55:05 +0800789 /* test CRCX with early bi-directional mode, expect 527 as
790 * bi-diretional media can only be established once both local and
791 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800792 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
793 var template MgcpCommand cmd;
794 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100795 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100796 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800797 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
798
Harald Welteedc45c12017-11-18 19:15:05 +0100799 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800800
Harald Welteba62c8c2017-11-18 18:26:49 +0100801 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800802 resp := mgcp_transceive_mgw(cmd, rtmpl);
803 setverdict(pass);
804 }
805
806 /* test CRCX with unsupported Parameters */
807 testcase TC_crcx_unsupp_param() runs on dummy_CT {
808 var template MgcpCommand cmd;
809 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100810 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100811 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800812 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
813
Harald Welteedc45c12017-11-18 19:15:05 +0100814 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800815
Harald Welteba62c8c2017-11-18 18:26:49 +0100816 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100817 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
818 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
819
Harald Weltee636afd2017-09-17 16:24:09 +0800820 resp := mgcp_transceive_mgw(cmd, rtmpl);
821 setverdict(pass);
822 }
823
824 /* test CRCX with missing CallId */
825 testcase TC_crcx_missing_callid() runs on dummy_CT {
826 var template MgcpCommand cmd;
827 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100828 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100829 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800830
Harald Welteedc45c12017-11-18 19:15:05 +0100831 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800832
Harald Welteba62c8c2017-11-18 18:26:49 +0100833 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800834 cmd.params := {
835 t_MgcpParConnMode("recvonly"),
836 t_MgcpParLocConnOpt("p:20")
837 }
838 resp := mgcp_transceive_mgw(cmd, rtmpl);
839 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100840
Harald Weltee636afd2017-09-17 16:24:09 +0800841 }
842
843 /* test CRCX with missing Mode */
844 testcase TC_crcx_missing_mode() runs on dummy_CT {
845 var template MgcpCommand cmd;
846 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100847 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100848 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100849 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800850
Harald Welteedc45c12017-11-18 19:15:05 +0100851 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800852
Harald Welteba62c8c2017-11-18 18:26:49 +0100853 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800854 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100855 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800856 t_MgcpParLocConnOpt("p:20")
857 }
858 resp := mgcp_transceive_mgw(cmd, rtmpl);
859 setverdict(pass);
860 }
861
862 /* test CRCX with unsupported packetization interval */
863 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
864 var template MgcpCommand cmd;
865 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100866 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100867 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100868 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800869
Harald Welteedc45c12017-11-18 19:15:05 +0100870 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800871
Harald Welteba62c8c2017-11-18 18:26:49 +0100872 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100873 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800874 resp := mgcp_transceive_mgw(cmd, rtmpl);
875 setverdict(pass);
876 }
877
878 /* test CRCX with illegal double presence of local connection option */
879 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
880 var template MgcpCommand cmd;
881 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100882 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100883 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800884 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
885
Harald Welteedc45c12017-11-18 19:15:05 +0100886 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800887
Harald Welteba62c8c2017-11-18 18:26:49 +0100888 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100889 /* p:20 is permitted only once and not twice! */
890 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800891 resp := mgcp_transceive_mgw(cmd, rtmpl);
892 setverdict(pass);
893 }
894
895 /* test valid CRCX with valid SDP */
896 testcase TC_crcx_sdp() runs on dummy_CT {
897 var template MgcpCommand cmd;
898 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100899 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100900 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800901
Harald Welteedc45c12017-11-18 19:15:05 +0100902 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800903
Harald Welteba62c8c2017-11-18 18:26:49 +0100904 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800905 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
906 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
907 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100908 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100909
910 /* clean-up */
911 f_dlcx_ok(ep, call_id);
912
Harald Weltee636afd2017-09-17 16:24:09 +0800913 setverdict(pass);
914 }
915
Philipp Maier5e06cee2018-02-01 18:28:08 +0100916 /* test valid wildcarded CRCX */
917 testcase TC_crcx_wildcarded() runs on dummy_CT {
918 var template MgcpCommand cmd;
919 var MgcpResponse resp;
920 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
921 var MgcpCallId call_id := '1234'H;
922 var MgcpEndpoint ep_assigned;
923 f_init();
924
925 /* create the connection on the MGW */
926 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
927 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
928 extract_conn_id(resp);
929
930 /* extract endpoint name we got assigned by the MGW */
931 var MgcpMessage resp_msg := {
932 response := resp
933 }
934 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
935 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200936 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100937 }
938
939 /* clean-up */
940 f_dlcx_ok(ep_assigned, call_id);
941
942 setverdict(pass);
943 }
944
945 /* test valid wildcarded CRCX */
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200946 type record of MgcpEndpoint MgcpEndpointList;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100947 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maier5e06cee2018-02-01 18:28:08 +0100948 var integer i;
949 var template MgcpCommand cmd;
950 var MgcpResponse resp;
951 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
952 var MgcpCallId call_id := '1234'H;
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200953 var MgcpEndpoint ep_assigned;
954 var MgcpEndpointList ep_assigned_li := {};
Philipp Maier5e06cee2018-02-01 18:28:08 +0100955 f_init();
956
957 /* Exhaust all endpoint resources on the virtual trunk */
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200958 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier5e06cee2018-02-01 18:28:08 +0100959 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
960 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
961
962 /* Make sure we got a connection id */
963 extract_conn_id(resp);
964
965 var MgcpMessage resp_msg := {
966 response := resp
967 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200968 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
Philipp Maier5e06cee2018-02-01 18:28:08 +0100969 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200970 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100971 }
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200972 ep_assigned_li := ep_assigned_li & {ep_assigned}
Philipp Maier5e06cee2018-02-01 18:28:08 +0100973 }
974
975 /* Try to allocate one more endpoint, which should fail */
976 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
977 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
978 resp := mgcp_transceive_mgw(cmd, rtmpl);
979 setverdict(pass);
980
981 /* clean-up */
Pau Espin Pedrole7928872022-10-07 10:57:11 +0200982 for (i := 0; i < mp_num_endpoints; i := i+1) {
983 f_dlcx_ok(ep_assigned_li[i], call_id);
Philipp Maier5e06cee2018-02-01 18:28:08 +0100984 }
985 setverdict(pass);
986 }
987
Harald Weltee636afd2017-09-17 16:24:09 +0800988 /* TODO: various SDP related bits */
989
990
991 /* TODO: CRCX with X-Osmux */
992 /* TODO: double CRCX without force_realloc */
993
994 /* TODO: MDCX (various) */
995
996 /* TODO: MDCX without CRCX first */
997 testcase TC_mdcx_without_crcx() runs on dummy_CT {
998 var template MgcpCommand cmd;
999 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001000 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +01001001 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +08001002 var template MgcpResponse rtmpl := {
1003 line := {
1004 /* TODO: accept/enforce better error? */
1005 code := "400",
1006 string := ?
1007 },
1008 params:= { },
1009 sdp := omit
1010 };
1011
Harald Welteedc45c12017-11-18 19:15:05 +01001012 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001013
Harald Welte2bcfd3a2017-11-18 22:14:35 +01001014 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001015 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1016 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1017 valueof(ts_SDP_ptime(20)) });
1018 resp := mgcp_transceive_mgw(cmd, rtmpl);
1019 setverdict(pass);
1020 }
1021
1022 /* DLCX without CRCX first */
1023 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1024 var template MgcpCommand cmd;
1025 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001026 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001027 var template MgcpResponse rtmpl := {
1028 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001029 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001030 string := ?
1031 },
1032 params:= { },
1033 sdp := omit
1034 };
1035
Harald Welteedc45c12017-11-18 19:15:05 +01001036 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001037
Harald Welteedc45c12017-11-18 19:15:05 +01001038 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001039 resp := mgcp_transceive_mgw(cmd, rtmpl);
1040 setverdict(pass);
1041 }
1042
Philipp Maier21c1cff2021-07-20 14:22:53 +02001043 /* DLCX to non existing endpoint */
1044 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1045 var template MgcpCommand cmd;
1046 var MgcpResponse resp;
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001047 var charstring non_existant_ep := hex2str(int2hex(mp_num_endpoints + 1, 4))
1048 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & non_existant_ep & "@" & c_mgw_domain;
Philipp Maier21c1cff2021-07-20 14:22:53 +02001049 var template MgcpResponse rtmpl := {
1050 line := {
1051 code := ("500"),
1052 string := ?
1053 },
1054 params:= { },
1055 sdp := omit
1056 };
1057
1058 f_init(ep);
1059
1060 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1061 resp := mgcp_transceive_mgw(cmd, rtmpl);
1062 setverdict(pass);
1063 }
1064
Philipp Maier8a3dc922018-02-02 14:55:12 +01001065 /* test valid wildcarded MDCX */
1066 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1067 /* Note: A wildcarded MDCX is not allowed, so we expect the
1068 * MGW to reject this request */
1069 var template MgcpCommand cmd;
1070 var MgcpResponse resp;
1071 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1072 var MgcpCallId call_id := '1225'H;
1073 var template MgcpResponse rtmpl := {
1074 line := {
1075 /* TODO: accept/enforce better error? */
1076 code := "507",
1077 string := ?
1078 },
1079 params:= { },
1080 sdp := omit
1081 };
1082
1083 f_init(ep);
1084
1085 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1086 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1087 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1088 valueof(ts_SDP_ptime(20)) });
1089 resp := mgcp_transceive_mgw(cmd, rtmpl);
1090 setverdict(pass);
1091 }
1092
1093 /* test valid wildcarded DLCX */
1094 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001095 var template MgcpCommand cmd;
1096 var MgcpResponse resp;
1097 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001098 var integer i;
1099 var MgcpCallId call_id := '1234'H;
1100 var StatsDExpects expect;
1101 f_init(ep);
1102
1103 /* Allocate a few endpoints */
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001104 for (i := 0; i < mp_num_endpoints; i := i+1) {
Philipp Maier55b90542021-07-02 12:33:19 +02001105 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1106 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1107 }
1108
Philipp Maier1298b092021-11-18 18:33:39 +01001109 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
1110 * occupied endpoints */
1111 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001112 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001113 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := mp_num_endpoints, max := mp_num_endpoints}
Philipp Maier55b90542021-07-02 12:33:19 +02001114 };
1115 f_statsd_expect(expect);
1116
1117 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001118 var template MgcpResponse rtmpl := {
1119 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001120 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001121 string := ?
1122 },
1123 params:= { },
1124 sdp := omit
1125 };
Philipp Maier55b90542021-07-02 12:33:19 +02001126 cmd := ts_DLCX(get_next_trans_id(), ep);
1127 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001128
Philipp Maier6c740e82022-06-30 12:04:34 +02001129 /* Query a the statsd once to ensure that intermediate results are pulled from the
1130 * pipeline. The second query (below) will return the actual result. */
1131 expect := {
Pau Espin Pedrole7928872022-10-07 10:57:11 +02001132 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := mp_num_endpoints}
Philipp Maier6c740e82022-06-30 12:04:34 +02001133 };
1134 f_statsd_expect(expect);
1135
1136 /* The second query must resturn a result with 0 endpoints in use. */
Philipp Maier55b90542021-07-02 12:33:19 +02001137 expect := {
1138 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1139 };
1140 f_statsd_expect(expect);
1141
Philipp Maier8a3dc922018-02-02 14:55:12 +01001142 setverdict(pass);
1143 }
1144
Harald Welte79181ff2017-11-18 19:26:11 +01001145 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1146 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1147 var template MgcpCommand cmd;
1148 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001149 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001150 var MgcpCallId call_id := '51234'H;
1151
1152 f_init(ep);
1153
1154 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1155 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1156
1157 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1158
1159 setverdict(pass);
1160 }
1161
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001162 /* test valid CRCX without SDP */
1163 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1164 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1165 setverdict(pass);
1166 }
1167
1168 /* test valid CRCX without SDP */
1169 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1170 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1171 setverdict(pass);
1172 }
1173
1174 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1175 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1176 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1177 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1178 setverdict(pass);
1179 }
1180
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02001181 /* test Creating 257 concurrent osmux conns. It should fail since maximum is 256. */
1182 testcase TC_crcx_osmux_257() runs on dummy_CT {
1183 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1184 var template MgcpCommand cmd;
1185 var MgcpResponse resp;
1186 var charstring cid_response;
1187 var integer i;
1188
1189 f_init(ep, true);
1190
1191 for (i := 0; i < 256; i := i + 1) {
1192
1193 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1194 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
1195 extract_conn_id(resp);
1196
1197 /* extract Osmux CID we got assigned by the MGW */
1198 var MgcpMessage resp_msg := {
1199 response := resp
1200 }
1201
1202 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
1203 setverdict(fail, "No Osmux CID in MGCP response", resp);
1204 mtc.stop;
1205 }
1206
1207 /* Make sure response is no wildcard */
1208 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
1209 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
1210 mtc.stop;
1211 }
1212 }
1213
1214 /* Now conn num 257, it should fail due to all Osmux conns already allocated: */
1215 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1);
1216 resp := mgcp_transceive_mgw(cmd, tr_MgcpResp_Err("400"));
1217
1218 setverdict(pass);
1219
1220 /* Clean up */
1221 for (i := 0; i < 256; i := i + 1) {
1222 f_dlcx_ok(ep, int2hex(i, 4));
1223 }
1224 }
1225
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001226 /* Create one half open connection in receive-only mode. The MGW must accept
1227 * the packets but must not send any. */
1228 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1229 var RtpFlowData flow;
1230 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1231 var MgcpCallId call_id := '1225'H;
1232 var OsmuxemStats stats;
1233 var OsmuxTxHandle tx_hdl;
1234
1235 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001236 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001237 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001238 flow.osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001239 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1240
1241 /* create a transmitter not yet known by MGW */
1242 tx_hdl := valueof(t_TxHandleAMR590(2));
1243 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1244
1245 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1246 f_sleep(1.0);
1247 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1248
1249 stats := f_osmuxem_stats_get(OsmuxEM);
1250
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001251 if (stats.num_pkts_tx < 40 / flow.osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001252 setverdict(fail);
1253 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001254 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001255 setverdict(fail);
1256 }
1257
1258 f_osmuxem_stats_err_check(stats);
1259
1260 setverdict(pass);
1261 }
1262
1263 /* Create one connection in loopback mode, test if the Osmux packets are
1264 * actually reflected */
1265 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1266 var RtpFlowData flow;
1267 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1268 var MgcpCallId call_id := '1225'H;
1269 var OsmuxemStats stats;
1270 var OsmuxTxHandle tx_hdl;
1271
1272 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001273 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001274 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001275 flow.osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001276 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1277
1278 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1279 f_sleep(1.0);
1280
1281 /* Switch off both Tx, wait to receive delayed frames from MGW */
1282 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1283 f_sleep(0.1);
1284 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1285
1286 stats := f_osmuxem_stats_get(OsmuxEM);
1287
1288 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1289 setverdict(fail);
1290 }
1291 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1292 setverdict(fail);
1293 }
1294
1295 f_osmuxem_stats_err_check(stats);
1296
1297 setverdict(pass);
1298 }
1299
1300 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1301 * must match the reception statistics on the other side and vice versa. The
1302 * user may also supply a tolerance value (number of packets) when deviations
1303 * are acceptable */
1304 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1305 var integer plen;
1306
1307 log("stats A: ", a);
1308 log("stats B: ", b);
1309 log("tolerance: ", tolerance, " packets");
1310 log("batch_size: ", batch_size, " packets");
1311
1312 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1313
1314 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1315 return false;
1316 }
1317
1318 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1319 return false;
1320 }
1321
1322 if(a.num_pkts_tx > 0) {
1323 plen := a.bytes_payload_tx / a.num_pkts_tx;
1324 } else {
1325 plen := 0;
1326 }
1327
1328 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1329 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1330 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1331 return false;
1332 }
1333
1334 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) {
1335 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1336 return false;
1337 }
1338
1339 return true;
1340 }
1341
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001342 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1343 charstring local_ip_rtp, charstring remote_ip_rtp,
1344 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001345 var RtpFlowData flow[2];
1346 var RtpemStats stats_rtp;
1347 var OsmuxemStats stats_osmux;
1348 var MgcpResponse resp;
1349 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1350 var MgcpCallId call_id := '1226'H;
1351 var integer tolerance := 0;
1352
1353 f_init(ep, true);
1354
1355 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001356 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001357 flow[0].rtp_cfg := c_RtpemDefaultCfg
1358 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1359 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1360 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);
1361 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1362 /* bind local RTP emulation sockets */
1363 flow[0].em.portnr := 10000;
1364 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1365
1366 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001367 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001368 flow[1].em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001369 flow[1].osmux.local_cid := 2;
1370 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001371 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1372
1373 if (bidir) {
1374 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1375 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1376
1377 /* Note: When we test bidirectional we may
1378 * loose packets during switch off because
1379 * both ends are transmitting and we only
1380 * can switch them off one by one. */
1381 tolerance := 3;
1382 } else {
1383 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1384 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1385 }
1386
1387 f_sleep(1.0);
1388
1389 /* Switch off both Tx, wait to receive delayed frames from MGW */
1390 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1391 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1392 f_sleep(0.1);
1393
1394 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1395 f_flow_delete(RTPEM[1]);
1396
1397 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1398 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001399 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux.cfg.batch_size, tolerance)) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001400 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1401 mtc.stop;
1402 }
1403
1404 f_rtpem_stats_err_check(stats_rtp);
1405 f_osmuxem_stats_err_check(stats_osmux);
1406
1407 setverdict(pass);
1408 }
1409
1410 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1411 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001412 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1413 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001414 }
1415
1416 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1417 * exchange some data in both directions */
1418 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001419 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1420 mp_local_ipv4, mp_remote_ipv4);
1421 }
1422
1423 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1424 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1425 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1426 mp_local_ipv6, mp_remote_ipv6);
1427 }
1428 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1429 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1430 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1431 mp_local_ipv6, mp_remote_ipv6);
1432 }
1433 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1434 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1435 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1436 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001437 }
1438
1439
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001440 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1441 charstring local_ip_rtp, charstring remote_ip_rtp,
1442 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001443 var RtpFlowData flow[2];
1444 var RtpemStats stats_rtp;
1445 var OsmuxemStats stats_osmux;
1446 var MgcpResponse resp;
1447 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1448 var MgcpCallId call_id := '1227'H;
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001449 var integer num_pkts_tx[2], num_pkts_rx[2];
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001450 var integer temp;
1451
1452 f_init(ep, true);
1453
1454 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001455 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001456 flow[0].rtp_cfg := c_RtpemDefaultCfg
1457 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1458 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1459 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);
1460 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1461 /* bind local RTP emulation sockets */
1462 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001463 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001464
1465
1466 /* Create the second connection. This connection will be also
1467 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001468 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001469 flow[1].em.portnr := mp_local_osmux_port;
1470 if (crcx_osmux_wildcard) {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001471 flow[1].osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001472 } else {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001473 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001474 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001475 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001476 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001477
1478
1479 /* The first leg starts transmitting */
1480 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1481 f_sleep(0.5);
1482 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1483 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1484 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1485 mtc.stop;
1486 }
1487 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1488 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1489 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1490 mtc.stop;
1491 }
1492
1493 /* The second leg starts transmitting a little later */
1494 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1495 f_sleep(1.0);
1496 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1497 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1498 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1499 mtc.stop;
1500 }
1501 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1502 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1503 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1504 mtc.stop;
1505 }
1506
1507 /* The first leg will now be switched into bidirectional
1508 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001509 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001510 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001511 /* At this point in time, flow[1](Osmux)->MGW->flow[0](RTP) is active,
1512 * hence if local CID was provided during CRCX we should already be seeing packets
1513 * flowing in one direction, aka stats_rtp.num_pkts_rx sould be >0 after a while: */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001514 f_sleep(0.5);
1515 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1516 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1517 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1518 mtc.stop;
1519 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001520 if (not crcx_osmux_wildcard and stats_rtp.num_pkts_rx == 0) {
1521 setverdict(fail, "received 0 packets Osmux->MGW->RTP");
1522 mtc.stop;
1523 }
1524
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001525 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1526 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1527 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1528 mtc.stop;
1529 }
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001530 if (stats_osmux.num_pkts_rx > 0) {
1531 setverdict(fail, "received unexpected ", stats_osmux.num_pkts_rx, " packets RTP->MGW->Osmux");
1532 mtc.stop;
1533 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001534
1535 /* When the second leg is switched into bidirectional mode
1536 * as well, then the MGW will connect the two together and
1537 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001538 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001539 if (crcx_osmux_wildcard) {
Pau Espin Pedrol982b9792022-10-07 11:48:13 +02001540 /* We set now the local CID in MDCX: */
1541 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001542 }
1543 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001544 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1545 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1546 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
1547 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
1548 num_pkts_rx[0] := stats_rtp.num_pkts_rx;
1549 num_pkts_rx[1] := stats_osmux.num_pkts_rx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001550 f_sleep(2.0);
1551
1552 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1553 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1554
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001555 temp := (stats_rtp.num_pkts_tx - num_pkts_tx[0]) -
1556 (stats_osmux.num_pkts_rx - num_pkts_rx[1]) * flow[1].osmux.cfg.batch_size;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001557 if (temp > 3 * flow[1].osmux.cfg.batch_size or temp < -3 * flow[1].osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001558 log("stats_rtp: ", stats_rtp);
1559 log("stats_osmux: ", stats_osmux);
1560 log("old_rtp_tx: ", num_pkts_tx[0]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001561 setverdict(fail, "RTP-Tx vs OSmux-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001562 mtc.stop;
1563 }
1564
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001565 temp := (stats_osmux.num_pkts_tx - num_pkts_tx[1]) -
1566 ((stats_rtp.num_pkts_rx - num_pkts_rx[0])/ flow[1].osmux.cfg.batch_size);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001567 if (temp > 3 or temp < -3) {
Pau Espin Pedroldb1e0682022-10-06 19:40:29 +02001568 log("stats_rtp: ", stats_rtp);
1569 log("stats_osmux: ", stats_osmux);
1570 log("old_osmux_tx: ", num_pkts_tx[1]);
Pau Espin Pedrolf3699832022-10-06 19:54:46 +02001571 setverdict(fail, "Osmux-Tx vs RTP-Rx number of packets not within normal parameters (" & int2str(temp) & ")");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001572 mtc.stop;
1573 }
1574
1575 f_rtpem_stats_err_check(stats_rtp);
1576 f_osmuxem_stats_err_check(stats_osmux);
1577
1578 /* Tear down */
1579 f_flow_delete(RTPEM[0]);
1580 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1581 setverdict(pass);
1582 }
1583
1584 /* create one RTP and one OSmux emulations and pass data in both
1585 directions. Create CRCX with wildcard Osmux CID and set it later
1586 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1587 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001588 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1589 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001590 }
1591
1592 /* create one RTP and one OSmux emulations and pass data in both
1593 directions. Create CRCX with fixed Osmux CID and keep it during
1594 MDCX. This is similar to how BSC sets up the call in AoIP. */
1595 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001596 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1597 mp_local_ipv4, mp_remote_ipv4);
1598 }
1599
1600 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1601 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1602 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1603 mp_local_ipv6, mp_remote_ipv6);
1604 }
1605 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1606 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1607 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1608 mp_local_ipv6, mp_remote_ipv6);
1609 }
1610 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1611 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1612 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1613 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001614 }
1615
Harald Welte646ecdb2017-12-28 03:21:57 +01001616 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1617 var template MgcpCommand cmd;
1618 var MgcpResponse resp;
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_ok(ep, call_id, extract_conn_id(resp));
1624
1625 setverdict(pass);
1626 }
1627
1628 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1629 var MgcpEndpoint ep;
1630 var MgcpCallId call_id;
1631 var integer ep_nr;
1632
1633 f_init();
1634
1635 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001636 if(ep_nr > 15) {
1637 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1638 } else {
1639 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1640 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001641 call_id := int2hex(ep_nr, 2) & '1234'H;
1642 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1643 }
1644 }
1645
Harald Welte79181ff2017-11-18 19:26:11 +01001646 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1647 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001648 var template MgcpCommand cmd;
1649 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001650 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001651 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001652
Harald Welteedc45c12017-11-18 19:15:05 +01001653 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001654
Harald Welteba62c8c2017-11-18 18:26:49 +01001655 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001656 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001657
Harald Welteba62c8c2017-11-18 18:26:49 +01001658 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001659
1660 setverdict(pass);
1661 }
1662
Harald Welte79181ff2017-11-18 19:26:11 +01001663 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1664 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1665 var template MgcpCommand cmd;
1666 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001667 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001668 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001669
1670 f_init(ep);
1671
1672 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1673 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1674
1675 f_dlcx_ok(ep);
1676
1677 setverdict(pass);
1678 }
1679
1680
Harald Welte6d167f82017-11-18 19:41:35 +01001681 /* CRCX + DLCX of valid endpoint but invalid call-id */
1682 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1683 var template MgcpCommand cmd;
1684 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001685 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001686 var MgcpCallId call_id := '51231'H;
1687
1688 f_init(ep);
1689
1690 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1691 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1692
1693 f_dlcx(ep, "516", *, 'ffff'H);
1694
1695 setverdict(pass);
1696 }
1697
1698
1699 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1700 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1701 var template MgcpCommand cmd;
1702 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001703 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001704 var MgcpCallId call_id := '51230'H;
1705
1706 f_init(ep);
1707
1708 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1709 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1710
1711 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1712
1713 setverdict(pass);
1714 }
1715
1716
Harald Weltee636afd2017-09-17 16:24:09 +08001717 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001718 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1719 var template MgcpCommand cmd;
1720 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001721 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001722 var MgcpCallId call_id := '51229'H;
1723 var template MgcpResponse rtmpl := {
1724 line := {
1725 code := "200",
1726 string := "OK"
1727 },
1728 params:= { },
1729 sdp := omit
1730 };
1731
1732 f_init(ep);
1733
1734 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1735 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1736
1737 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1738 resp := mgcp_transceive_mgw(cmd, rtmpl);
1739 resp := mgcp_transceive_mgw(cmd, rtmpl);
1740
1741 setverdict(pass);
1742 }
1743
Harald Weltebb7523b2018-03-29 08:52:01 +02001744 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1745 charstring codec) := {
1746 em := {
1747 hostname := host_a,
1748 portnr := omit
1749 },
1750 mgw := {
1751 hostname := host_b,
1752 portnr := omit
1753 },
1754 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001755 codec := codec,
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001756 osmux:= {
1757 local_cid_sent := false,
1758 local_cid := omit,
1759 remote_cid := omit,
1760 cfg := omit
1761 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001762 }
Harald Weltef53f1642017-11-18 19:57:11 +01001763
Harald Weltebb7523b2018-03-29 08:52:01 +02001764 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1765 testcase TC_rtpem_selftest() runs on dummy_CT {
1766 var RtpemStats stats[2];
1767 var integer local_port := 10000;
1768 var integer local_port2 := 20000;
1769
1770 f_init();
1771
1772 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1773 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1774
1775 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1776 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1777
1778 log("=== starting");
1779 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1780 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1781
1782 f_sleep(5.0);
1783
1784 log("=== stopping");
1785 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1786 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1787 f_sleep(0.5);
1788 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1789 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1790
1791 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1792 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1793 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1794 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001795 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001796 }
1797 setverdict(pass);
1798 }
1799
Philipp Maier2321ef92018-06-27 17:52:04 +02001800 /* Create one half open connection in receive-only mode. The MGW must accept
1801 * the packets but must not send any. */
1802 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1803 var RtpFlowData flow;
1804 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1805 var MgcpCallId call_id := '1225'H;
1806 var RtpemStats stats;
1807
1808 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001809 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001810 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001811 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001812
1813 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1814 f_sleep(1.0);
1815 f_flow_delete(RTPEM[0], ep, call_id);
1816
1817 stats := f_rtpem_stats_get(RTPEM[0]);
1818
Philipp Maier42b17cc2019-10-01 13:53:17 +02001819 /* Make sure that at least some amount of RTP packets/bytes
1820 * have has been transmitted. The compare values for
1821 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1822 * using a testrun and the results were devided by 2, so even
1823 * in load situations we should reach the minimum amount of
1824 * required packets/bytes */
1825
1826 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001827 setverdict(fail);
1828 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001829 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001830 setverdict(fail);
1831 }
Philipp Maier36291392018-07-25 09:40:44 +02001832
1833 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001834
1835 setverdict(pass);
1836 }
1837
1838 /* Create one connection in loopback mode, test if the RTP packets are
1839 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001840 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 +02001841 var RtpFlowData flow;
1842 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1843 var MgcpCallId call_id := '1225'H;
1844 var RtpemStats stats;
1845
1846 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001847 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001848 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001849 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001850
1851 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1852 f_sleep(1.0);
1853 f_flow_delete(RTPEM[0], ep, call_id);
1854
1855 stats := f_rtpem_stats_get(RTPEM[0]);
1856
1857 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1858 setverdict(fail);
1859 }
1860 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1861 setverdict(fail);
1862 }
Philipp Maier36291392018-07-25 09:40:44 +02001863
1864 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001865
1866 setverdict(pass);
1867 }
1868
Philipp Maier1ac13982021-05-07 23:06:07 +02001869 /* Create one connection in loopback mode, test if the RTP packets are
1870 * actually reflected */
1871 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001872 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001873 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001874 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1875 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1876 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001877
1878 /* Same as above, but we will intenionally not tell the MGW where to
1879 * send the outgoing traffic. The connection is still created in
1880 * loopback mode, so the MGW should take the originating address from
1881 * the incoming RTP packet and send it back to the source */
1882 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001883 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001884 }
1885
1886
Philipp Maier7df85f62018-07-25 10:26:09 +02001887 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1888 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001889 var RtpFlowData flow[2];
1890 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001891 var MgcpResponse resp;
1892 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1893 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001894 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001895
1896 f_init(ep);
1897
1898 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001899 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001900 /* bind local RTP emulation sockets */
1901 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001902 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001903
1904 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001905 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001906 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001907 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1908
1909 if (bidir) {
1910 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1911 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1912
1913 /* Note: When we test bidirectional we may
1914 * loose packets during switch off because
1915 * both ends are transmitting and we only
1916 * can switch them off one by one. */
1917 tolerance := 3;
1918 } else {
1919 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1920 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1921 }
1922
1923 f_sleep(1.0);
1924
1925 f_flow_delete(RTPEM[1]);
1926 f_flow_delete(RTPEM[0], ep, call_id);
1927
1928 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1929 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1930 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1931 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001932 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001933 }
1934
Philipp Maier36291392018-07-25 09:40:44 +02001935 f_rtpem_stats_err_check(stats[0]);
1936 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001937
Philipp Maier2321ef92018-06-27 17:52:04 +02001938 setverdict(pass);
1939 }
1940
1941 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1942 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001943 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001944 }
1945
1946 /* create two local RTP emulations; create two connections on MGW EP,
1947 * exchange some data in both directions */
1948 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001949 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1950 }
1951
1952 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1953 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1954 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1955 }
1956
1957 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1958 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1959 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001960 }
1961
1962 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001963 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1964 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001965 var RtpFlowData flow[2];
1966 var RtpemStats stats[2];
1967 var MgcpResponse resp;
1968 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1969 var MgcpCallId call_id := '1227'H;
1970 var integer num_pkts_tx[2];
1971 var integer temp;
1972
1973 f_init(ep);
1974
1975 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001976 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001977 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001978 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001979
1980 /* Create the second connection. This connection will be also
1981 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001982 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001983 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001984 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001985
1986 /* The first leg starts transmitting */
1987 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1988 f_sleep(0.5);
1989 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1990 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001991 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001992 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001993 }
1994 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1995 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001996 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001997 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001998 }
1999
2000 /* The second leg starts transmitting a little later */
2001 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2002 f_sleep(1.0);
2003 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2004 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002005 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002006 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002007 }
2008 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2009 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002010 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002011 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002012 }
2013
2014 /* The first leg will now be switched into bidirectional
2015 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002016 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2017 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2018 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002019 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2020 f_sleep(0.5);
2021 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002022 if (stats[0].num_pkts_rx_err_disabled != 0) {
2023 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02002024 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002025 }
2026 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2027 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002028 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02002029 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002030 }
2031
2032 /* When the second leg is switched into bidirectional mode
2033 * as well, then the MGW will connect the two together and
2034 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02002035 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2036 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002037 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02002038 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2039 f_sleep(2.0);
2040
2041 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2042 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2043
2044 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2045 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002046 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002047 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002048 }
2049
2050 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2051 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02002052 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02002053 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002054 }
Philipp Maier36291392018-07-25 09:40:44 +02002055
2056 f_rtpem_stats_err_check(stats[0]);
2057 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02002058
2059 /* Tear down */
2060 f_flow_delete(RTPEM[0]);
2061 f_flow_delete(RTPEM[1], ep, call_id);
2062 setverdict(pass);
2063 }
2064
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002065 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
2066 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2067 mp_local_ipv4, mp_remote_ipv4);
2068 }
2069
2070 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
2071 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
2072 mp_local_ipv6, mp_remote_ipv6);
2073 }
2074
2075 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
2076 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2077 mp_local_ipv6, mp_remote_ipv6);
2078 }
2079
Philipp Maier2321ef92018-06-27 17:52:04 +02002080 /* Test what happens when two RTP streams from different sources target
2081 * a single connection. Is the unsolicited stream properly ignored? */
2082 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2083 var RtpFlowData flow[2];
2084 var RtpemStats stats[2];
2085 var MgcpResponse resp;
2086 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2087 var MgcpCallId call_id := '1234321326'H;
2088 var integer unsolicited_port := 10002;
2089
2090 f_init(ep);
2091
2092 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002093 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002094 /* bind local RTP emulation sockets */
2095 flow[0].em.portnr := 10000;
2096 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2097
2098 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002099 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002100 flow[1].em.portnr := 20000;
2101 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002102
2103 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2104 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2105
Philipp Maier2321ef92018-06-27 17:52:04 +02002106 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002107
Philipp Maier2321ef92018-06-27 17:52:04 +02002108 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002109 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2110 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002111 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2112
2113 f_sleep(0.5);
2114
Daniel Willmanna069d382018-12-13 13:53:33 +01002115 /* Stop transmitting packets and tear down the flows */
2116 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002117 f_flow_delete(RTPEM[0]);
2118 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002119
2120 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2121 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2122 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2123 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002124 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002125 }
2126
Philipp Maier36291392018-07-25 09:40:44 +02002127 f_rtpem_stats_err_check(stats[0]);
2128 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002129
Harald Weltebb7523b2018-03-29 08:52:01 +02002130 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002131 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002132
Philipp Maier2321ef92018-06-27 17:52:04 +02002133 /* Test a handover situation. We first create two connections transmit
2134 * some data bidirectionally. Then we will simulate a handover situation. */
2135 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2136 var RtpFlowData flow[2];
2137 var RtpemStats stats[3];
2138 var MgcpResponse resp;
2139 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2140 var MgcpCallId call_id := '76338'H;
2141 var integer port_old;
2142
2143 f_init(ep);
2144
2145 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002146 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002147 /* bind local RTP emulation sockets */
2148 flow[0].em.portnr := 10000;
2149 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2150
2151 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002152 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002153 flow[1].em.portnr := 20000;
2154 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2155
2156 /* Normal rtp flow for one second */
2157 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2158 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2159 f_sleep(1.0);
2160
2161 /* Now switch the flow over to a new port (BTS) */
2162 port_old := flow[0].em.portnr;
2163 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002164 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002165 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002166 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002167
2168 /* When handing over a call, the old source may still keep
2169 * transmitting for a while. We simulate this by injecting
2170 * some unsolicited packets on the behalf of the old source,
2171 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002172 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2173 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002174 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2175 f_sleep(1.0);
2176 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2177 f_sleep(1.0);
2178
2179 /* Terminate call */
2180 f_flow_delete(RTPEM[0]);
2181 f_flow_delete(RTPEM[1], ep, call_id);
2182
2183 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2184 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2185 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2186 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002187 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002188 }
2189 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2190 if (stats[2].num_pkts_rx_err_disabled != 0) {
2191 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002192 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002193 }
2194
Philipp Maier36291392018-07-25 09:40:44 +02002195 f_rtpem_stats_err_check(stats[0]);
2196 f_rtpem_stats_err_check(stats[1]);
2197 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002198
Philipp Maier2321ef92018-06-27 17:52:04 +02002199 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002200 }
Harald Weltef53f1642017-11-18 19:57:11 +01002201
Philipp Maier6d4e0942019-02-21 17:35:01 +01002202
2203 /* create two local RTP emulations; create two connections on MGW EP, see if
2204 * exchanged data is converted bwtween ts101318 and rfc5993 */
2205 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2206 var RtpFlowData flow[2];
2207 var RtpemStats stats[2];
2208 var MgcpResponse resp;
2209 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2210 var MgcpCallId call_id := '1226'H;
2211
2212 f_init(ep);
2213
2214 /* Turn on conversion mode */
2215 f_vty_enter_config(MGWVTY);
2216 f_vty_transceive(MGWVTY, "mgcp");
2217 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2218
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002219 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002220 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002221 /* bind local RTP emulation sockets */
2222 flow[0].em.portnr := 10000;
2223 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2224 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2225 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2226 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2227 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2228
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002229 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002230 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002231 flow[1].em.portnr := 20000;
2232 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2233 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2234 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2235 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2236 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2237
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002238 /* Send RTP packets to connection #0, receive on connection #1 */
2239 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2240 f_sleep(0.5);
2241 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002242 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002243 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2244 f_sleep(0.5);
2245 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002246
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002247 /* Send RTP packets to connection #1, receive on connection #0 */
2248 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2249 f_sleep(0.5);
2250 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2251 f_sleep(1.0);
2252 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2253 f_sleep(0.5);
2254 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2255
2256 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002257 f_flow_delete(RTPEM[0]);
2258 f_flow_delete(RTPEM[1], ep, call_id);
2259
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002260 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002261 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2262 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002263 f_rtpem_stats_err_check(stats[0]);
2264 f_rtpem_stats_err_check(stats[1]);
2265
2266 /* Turn off conversion mode */
2267 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2268
2269 setverdict(pass);
2270 }
2271
Philipp Maier4f764ce2019-03-07 10:54:10 +01002272 /* create two local RTP emulations; create two connections on MGW EP, see if
2273 * exchanged data is converted between AMR octet-aligned and bandwith
2274 * efficient-mode */
2275 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2276 var RtpFlowData flow[2];
2277 var RtpemStats stats[2];
2278 var MgcpResponse resp;
2279 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2280 var MgcpCallId call_id := '1226'H;
2281
2282 f_init(ep);
2283
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002284 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002285 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002286 /* bind local RTP emulation sockets */
2287 flow[0].em.portnr := 10000;
2288 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2289 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2290 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2291 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2292 flow[0].fmtp := fmtp0;
2293 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2294
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002295 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002296 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002297 flow[1].em.portnr := 20000;
2298 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2299 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2300 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2301 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2302 flow[1].fmtp := fmtp1;
2303 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2304
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002305 /* Send RTP packets to connection #0, receive on connection #1 */
2306 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2307 f_sleep(0.5);
2308 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002309 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002310 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2311 f_sleep(0.5);
2312 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002313
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002314 /* Send RTP packets to connection #1, receive on connection #0 */
2315 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2316 f_sleep(0.5);
2317 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2318 f_sleep(1.0);
2319 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2320 f_sleep(0.5);
2321 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2322
2323 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002324 f_flow_delete(RTPEM[0]);
2325 f_flow_delete(RTPEM[1], ep, call_id);
2326
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002327 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002328 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2329 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002330 f_rtpem_stats_err_check(stats[0]);
2331 f_rtpem_stats_err_check(stats[1]);
2332
2333 setverdict(pass);
2334 }
2335
Philipp Maier882843d2020-05-25 15:33:13 +02002336 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2337 * functions are real world AMR RTP payloads including AMR header. The
2338 * payloads were extracted from a trace with known good payloads. */
2339
Philipp Maier4f764ce2019-03-07 10:54:10 +01002340 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002341 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002342 }
2343
2344 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2345 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2346 }
2347
2348 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2349 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2350 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002351
Harald Weltee636afd2017-09-17 16:24:09 +08002352 /* TODO: Double-DLCX (no retransmission) */
2353
2354
2355
2356 /* TODO: AUEP (various) */
2357 /* TODO: RSIP (various) */
2358 /* TODO: RQNT (various) */
2359 /* TODO: EPCF (various) */
2360 /* TODO: AUCX (various) */
2361 /* TODO: invalid verb (various) */
2362
Oliver Smith021141e2019-06-25 12:09:01 +02002363
2364 testcase TC_conn_timeout() runs on dummy_CT {
2365 var RtpFlowData flow;
2366 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2367 var MgcpCallId call_id := '1225'H;
2368 var MGCP_RecvFrom mrf;
2369
2370 f_init(ep);
2371 log("Setting conn-timeout to 1s");
2372 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2373
2374 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002375 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002376 flow.em.portnr := 10000;
2377 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2378 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2379 f_sleep(1.5);
2380
2381 log("Stopping for 0.5s and resuming");
2382 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2383 f_sleep(0.5);
2384 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2385 f_sleep(0.1);
2386
2387 log("Stopping for 1.5s, expecting to run into timeout");
2388 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2389 f_sleep(1.5);
2390
2391 log("Resuming should fail now");
2392 f_rtpem_conn_refuse_expect(RTPEM[0]);
2393 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2394 f_sleep(0.2);
2395 f_rtpem_conn_refuse_verify(RTPEM[0]);
2396
2397 setverdict(pass);
2398 }
2399
Philipp Maier2609c752020-07-08 12:38:09 +02002400 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2401 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2402 var template MgcpCommand cmd;
2403 var MgcpResponse resp;
2404 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2405 var MgcpCallId call_id := '8376F297'H;
2406
2407 f_init(ep);
2408
2409 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2410 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2411
2412 f_dlcx_ok(ep);
2413
2414 setverdict(pass);
2415 }
2416
2417 /* Test what happens when overlapping endpoints are selected (E1) */
2418 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2419 var template MgcpCommand cmd;
2420 var MgcpResponse resp;
2421 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2422 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2423 var MgcpCallId call_id_1 := '8376F297'H;
2424 var MgcpCallId call_id_2 := '837AF2A7'H;
2425
2426 f_init();
2427
2428 /* ep_1 and ep_2 are overlapping, selecting both one after
2429 * another should work fine: */
2430 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2431 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2432 f_dlcx_ok(ep_1);
2433 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2434 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2435 f_dlcx_ok(ep_2);
2436
2437 /* When ep_1 is serving a call we can not select ep_2 becaus
2438 * it is overlapping with ep_1 */
2439 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2440 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2441 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2442 resp := mgcp_transceive_mgw(cmd, ?);
2443 if (resp.line.code != "501") {
2444 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2445 }
2446 f_dlcx_ok(ep_1);
2447
2448 setverdict(pass);
2449 }
2450
2451 /* Create one connection in loopback mode, test if the RTP packets are
2452 * actually reflected */
2453 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2454 var RtpFlowData flow;
2455 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2456 var MgcpCallId call_id := '12250989'H;
2457 var RtpemStats stats;
2458
2459 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002460 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002461 flow.em.portnr := 10000;
2462 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2463
2464 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2465 f_sleep(1.0);
2466 f_flow_delete(RTPEM[0], ep, call_id);
2467
2468 stats := f_rtpem_stats_get(RTPEM[0]);
2469
2470 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2471 setverdict(fail);
2472 }
2473 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2474 setverdict(fail);
2475 }
2476
2477 f_rtpem_stats_err_check(stats);
2478
2479 setverdict(pass);
2480 }
2481
Philipp Maier13aff992022-06-30 16:19:59 +02002482 /* test valid wildcarded DLCX on an E1 trunk */
2483 testcase TC_e1_dlcx_wildcarded() runs on dummy_CT {
2484 var template MgcpCommand cmd;
2485 var MgcpEndpoint ep;
2486 var MgcpCallId call_id := '8376F297'H;
2487 var integer n_e1_ts := 4;
2488 var StatsDExpects expect;
2489
2490 f_init();
2491
2492 /* Open a few E1 timeslots */
2493 for (var integer i := 0; i < n_e1_ts; i := i+1) {
2494 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-0@" & c_mgw_domain;
2495 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2496 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2497 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-2@" & c_mgw_domain;
2498 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2499 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2500 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-4@" & c_mgw_domain;
2501 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2502 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2503 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-6@" & c_mgw_domain;
2504 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2505 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2506 }
2507
2508 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
2509 * occupied endpoints */
2510 f_sleep(1.0)
2511 expect := {
2512 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := n_e1_ts * 4, max := n_e1_ts * 4}
2513 };
2514 f_statsd_expect(expect);
2515
2516 /* Send wildcarded DLCX */
2517 var template MgcpResponse rtmpl := {
2518 line := {
2519 code := "200",
2520 string := ?
2521 },
2522 params:= { },
2523 sdp := omit
2524 };
2525 ep := "ds/e1-1/*@" & c_mgw_domain;
2526 cmd := ts_DLCX(get_next_trans_id(), ep);
2527 mgcp_transceive_mgw(cmd, rtmpl);
2528
2529 /* Query a the statsd once to ensure that intermediate results are pulled from the
2530 * pipeline. The second query (below) will return the actual result. */
2531 expect := {
2532 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := n_e1_ts * 4}
2533 };
2534 f_statsd_expect(expect);
2535
2536 /* The second query must resturn a result with 0 endpoints in use. */
2537 expect := {
2538 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := 0}
2539 };
2540 f_statsd_expect(expect);
2541
2542 setverdict(pass);
2543 }
2544
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002545 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2546 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2547 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2548 var template MgcpCommand cmd;
2549 var MgcpResponse resp;
2550 var MgcpCallId call_id := '1234'H;
2551 var MgcpConnectionId conn_id;
2552
2553 f_init(ep);
2554
2555 /* create the connection on the MGW */
2556 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2557 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2558 conn_id := extract_conn_id(resp);
2559
2560 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2561 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2562 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2563 valueof(ts_SDP_ptime(20)) });
2564 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2565
2566 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2567 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2568 }
2569 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2570 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2571 }
2572
2573 /* clean-up */
2574 f_dlcx_ok(ep, call_id);
2575 setverdict(pass);
2576 }
2577
2578 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2579 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2580 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2581 var template MgcpCommand cmd;
2582 var MgcpResponse resp;
2583 var MgcpCallId call_id := '1234'H;
2584 var MgcpConnectionId conn_id;
2585
2586 f_init(ep);
2587
2588 /* create the connection on the MGW */
2589 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2590 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2591 conn_id := extract_conn_id(resp);
2592
2593 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2594 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2595 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2596 valueof(ts_SDP_ptime(20)) });
2597 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2598
2599 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2600 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2601 }
2602 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2603 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2604 }
2605
2606 /* clean-up */
2607 f_dlcx_ok(ep, call_id);
2608 setverdict(pass);
2609 }
2610
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002611 /* create two local RTP+IuUP emulations and pass data in both directions */
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002612 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002613 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002614 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2615 var RtpFlowData flow[2];
2616 var RtpemStats stats[2];
2617 var MgcpResponse resp;
2618 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2619 var MgcpCallId call_id := '1227'H;
2620 var integer num_pkts_tx[2];
2621 var integer temp;
2622
2623 f_init(ep);
2624
2625 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2626 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2627 flow[0].em.portnr := 10000;
2628 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2629 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2630 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002631 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002632 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002633 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2634 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2635
2636 /* Create the second connection. This connection will be also
2637 * in receive only mode (CN side, IuUP-Init passive) */
2638 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2639 flow[1].em.portnr := 20000;
2640 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2641 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2642 flow[1].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002643 flow[1].rtp_cfg.iuup_cfg.active_init := false;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002644 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2645 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2646
2647 /* The first leg starts transmitting */
2648 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2649 f_sleep(0.5);
2650 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2651 if (stats[0].num_pkts_rx_err_disabled != 0) {
2652 setverdict(fail, "received packets from MGW on recvonly connection 0");
2653 mtc.stop;
2654 }
2655 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2656 if (stats[1].num_pkts_rx_err_disabled != 0) {
2657 setverdict(fail, "received packets from MGW on recvonly connection 1");
2658 mtc.stop;
2659 }
2660
2661 /* The second leg starts transmitting a little later */
2662 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2663 f_sleep(1.0);
2664 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2665 if (stats[0].num_pkts_rx_err_disabled != 0) {
2666 setverdict(fail, "received packets from MGW on recvonly connection 0");
2667 mtc.stop;
2668 }
2669 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2670 if (stats[1].num_pkts_rx_err_disabled != 0) {
2671 setverdict(fail, "received packets from MGW on recvonly connection 1");
2672 mtc.stop;
2673 }
2674
2675 /* The first leg will now be switched into bidirectional
2676 * mode, but we do not expect any data coming back yet. */
2677 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2678 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2679 num_pkts_tx[1] := stats[1].num_pkts_tx;
2680 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2681 f_sleep(0.5);
2682 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2683 if (stats[0].num_pkts_rx_err_disabled != 0) {
2684 setverdict(fail, "received packets from MGW on recvonly connection 0");
2685 mtc.stop;
2686 }
2687 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2688 if (stats[1].num_pkts_rx_err_disabled != 0) {
2689 setverdict(fail, "received packets from MGW on recvonly connection 1");
2690 mtc.stop;
2691 }
2692
2693 /* When the second leg is switched into bidirectional mode
2694 * as well, then the MGW will connect the two together and
2695 * we should see RTP streams passing through from both ends. */
2696 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2697 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2698 num_pkts_tx[0] := stats[0].num_pkts_tx;
2699 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2700 f_sleep(2.0);
2701
2702 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2703 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2704
2705 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2706 if (temp > 3 or temp < -3) {
2707 setverdict(fail, "number of packets not within normal parameters:", temp);
2708 mtc.stop;
2709 }
2710
2711 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2712 if (temp > 3 or temp < -3) {
2713 setverdict(fail, "number of packets not within normal parameters:", temp);
2714 mtc.stop;
2715 }
2716
2717 f_rtpem_stats_err_check(stats[0]);
2718 f_rtpem_stats_err_check(stats[1]);
2719
2720 /* Tear down */
2721 f_flow_delete(RTPEM[0]);
2722 f_flow_delete(RTPEM[1], ep, call_id);
2723 setverdict(pass);
2724 }
2725 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002726 var template (value) IuUP_RabFlowCombinationList rfcl := {
2727 t_IuUP_RFC_AMR_12_2(0),
2728 t_IuUP_RFC_AMR_SID(1),
2729 t_IuUP_RFC_AMR_NO_DATA(2)
2730 };
2731 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2732 mp_local_ipv4, mp_remote_ipv4);
2733 }
2734 /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */
2735 testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT {
2736 var template (value) IuUP_RabFlowCombinationList rfcl := {
2737 t_IuUP_RFC_AMR_12_2(1),
2738 t_IuUP_RFC_AMR_SID(2),
2739 t_IuUP_RFC_AMR_NO_DATA(0)
2740 };
2741 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002742 mp_local_ipv4, mp_remote_ipv4);
2743 }
2744
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002745 /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */
2746 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 +02002747 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002748 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2749 var RtpFlowData flow[2];
2750 var RtpemStats stats[2];
2751 var MgcpResponse resp;
2752 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2753 var MgcpCallId call_id := '1227'H;
2754 var integer num_pkts_tx[2];
2755 var integer temp;
2756
2757 f_init(ep);
2758
2759 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2760 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2761 flow[0].em.portnr := 10000;
2762 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2763 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2764 flow[0].rtp_cfg.tx_fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
2765 flow[0].rtp_cfg.rx_fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O; /* flow[1].rtp_cfg.tx_fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
2766 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002767 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002768 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002769 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2770 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2771
2772 /* Create the second connection. This connection will be also
2773 * in receive only mode (CN side, regular RTP) */
2774 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
2775 flow[1].em.portnr := 20000;
2776 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2777 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2778 flow[1].rtp_cfg.tx_fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
2779 flow[1].rtp_cfg.rx_fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O; /* flow[0].rtp_cfg.tx_fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
2780 flow[1].rtp_cfg.iuup_mode := false;
2781 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2782 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2783
2784 /* The first leg starts transmitting */
2785 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2786 f_sleep(0.5);
2787 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2788 if (stats[0].num_pkts_rx_err_disabled != 0) {
2789 setverdict(fail, "received packets from MGW on recvonly connection 0");
2790 mtc.stop;
2791 }
2792 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2793 if (stats[1].num_pkts_rx_err_disabled != 0) {
2794 setverdict(fail, "received packets from MGW on recvonly connection 1");
2795 mtc.stop;
2796 }
2797
2798 /* The second leg starts transmitting a little later */
2799 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2800 f_sleep(1.0);
2801 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2802 if (stats[0].num_pkts_rx_err_disabled != 0) {
2803 setverdict(fail, "received packets from MGW on recvonly connection 0");
2804 mtc.stop;
2805 }
2806 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2807 if (stats[1].num_pkts_rx_err_disabled != 0) {
2808 setverdict(fail, "received packets from MGW on recvonly connection 1");
2809 mtc.stop;
2810 }
2811
2812 /* The first leg will now be switched into bidirectional
2813 * mode, but we do not expect any data coming back yet. */
2814 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2815 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2816 num_pkts_tx[1] := stats[1].num_pkts_tx;
2817 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2818 f_sleep(0.5);
2819 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2820 if (stats[0].num_pkts_rx_err_disabled != 0) {
2821 setverdict(fail, "received packets from MGW on recvonly connection 0");
2822 mtc.stop;
2823 }
2824 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2825 if (stats[1].num_pkts_rx_err_disabled != 0) {
2826 setverdict(fail, "received packets from MGW on recvonly connection 1");
2827 mtc.stop;
2828 }
2829
2830 /* When the second leg is switched into bidirectional mode
2831 * as well, then the MGW will connect the two together and
2832 * we should see RTP streams passing through from both ends. */
2833 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2834 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2835 num_pkts_tx[0] := stats[0].num_pkts_tx;
2836 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2837 f_sleep(2.0);
2838
2839 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2840 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2841
2842 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2843 if (temp > 3 or temp < -3) {
2844 setverdict(fail, "number of packets not within normal parameters:", temp);
2845 mtc.stop;
2846 }
2847
2848 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2849 if (temp > 3 or temp < -3) {
2850 setverdict(fail, "number of packets not within normal parameters:", temp);
2851 mtc.stop;
2852 }
2853
2854 f_rtpem_stats_err_check(stats[0]);
2855 f_rtpem_stats_err_check(stats[1]);
2856
2857 /* Tear down */
2858 f_flow_delete(RTPEM[0]);
2859 f_flow_delete(RTPEM[1], ep, call_id);
2860 setverdict(pass);
2861 }
2862 testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002863 var template (value) IuUP_RabFlowCombinationList rfcl := {
2864 t_IuUP_RFC_AMR_12_2(0),
2865 t_IuUP_RFC_AMR_SID(1),
2866 t_IuUP_RFC_AMR_NO_DATA(2)
2867 };
2868 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2869 mp_local_ipv4, mp_remote_ipv4);
2870 }
2871 /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */
2872 testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT {
2873 var template (value) IuUP_RabFlowCombinationList rfcl := {
2874 t_IuUP_RFC_AMR_12_2(1),
2875 t_IuUP_RFC_AMR_SID(2),
2876 t_IuUP_RFC_AMR_NO_DATA(0)
2877 };
2878 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002879 mp_local_ipv4, mp_remote_ipv4);
2880 }
2881
Harald Welte00a067f2017-09-13 23:27:17 +02002882 control {
2883 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002884 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002885 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002886 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002887 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002888 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002889 execute(TC_crcx_early_bidir_mode());
2890 execute(TC_crcx_unsupp_param());
2891 execute(TC_crcx_missing_callid());
2892 execute(TC_crcx_missing_mode());
2893 execute(TC_crcx_unsupp_packet_intv());
2894 execute(TC_crcx_illegal_double_lco());
2895 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002896 execute(TC_crcx_wildcarded());
2897 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002898 execute(TC_mdcx_without_crcx());
2899 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02002900 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002901 execute(TC_mdcx_wildcarded());
2902 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002903 execute(TC_crcx_and_dlcx_ep_callid_connid());
2904 execute(TC_crcx_and_dlcx_ep_callid());
2905 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002906 execute(TC_crcx_and_dlcx_ep_callid_inval());
2907 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002908 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002909
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002910 execute(TC_crcx_osmux_wildcard());
2911 execute(TC_crcx_osmux_fixed());
2912 execute(TC_crcx_osmux_fixed_twice());
Pau Espin Pedrol787e2e42022-10-07 10:58:35 +02002913 execute(TC_crcx_osmux_257());
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002914 execute(TC_one_crcx_receive_only_osmux());
2915 execute(TC_one_crcx_loopback_osmux());
2916 execute(TC_two_crcx_and_rtp_osmux());
2917 execute(TC_two_crcx_and_rtp_osmux_bidir());
2918 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2919 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2920
Harald Welte33d82162017-12-28 03:21:57 +01002921 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002922
2923 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002924
2925 execute(TC_one_crcx_receive_only_rtp());
2926 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002927 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02002928 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002929 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002930 execute(TC_two_crcx_diff_pt_and_rtp());
2931 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002932 execute(TC_two_crcx_mdcx_and_rtp());
2933 execute(TC_two_crcx_and_unsolicited_rtp());
2934 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002935 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002936 execute(TC_amr_oa_bwe_rtp_conversion());
2937 execute(TC_amr_oa_oa_rtp_conversion());
2938 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002939
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002940 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002941
2942 execute(TC_e1_crcx_and_dlcx_ep());
2943 execute(TC_e1_crcx_with_overlap());
2944 execute(TC_e1_crcx_loopback());
Philipp Maier13aff992022-06-30 16:19:59 +02002945 execute(TC_e1_dlcx_wildcarded());
Philipp Maier2609c752020-07-08 12:38:09 +02002946
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002947 execute(TC_crcx_mdcx_ip4());
2948 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002949 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2950 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2951 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2952 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2953 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2954 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2955 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2956 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02002957
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002958 execute(TC_two_crcx_mdcx_and_iuup());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002959 execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered());
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002960 execute(TC_two_crcx_mdcx_and_iuup_rtp());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002961 execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered());
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002962
Philipp Maier37965082021-05-25 16:44:25 +02002963 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
2964 * older versions of osmo-mgw. This eventually leads into
2965 * a failure of all subsequent testcases, so it is important
2966 * not to add new testcaes after this one. */
2967 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02002968 }
2969}