blob: c54fdb4289b6b5032a6e7346e6ab84e6dfbfbbb2 [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"): */
88 integer mp_num_endpoints := 31;
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
1181 /* Create one half open connection in receive-only mode. The MGW must accept
1182 * the packets but must not send any. */
1183 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1184 var RtpFlowData flow;
1185 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1186 var MgcpCallId call_id := '1225'H;
1187 var OsmuxemStats stats;
1188 var OsmuxTxHandle tx_hdl;
1189
1190 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001191 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001192 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001193 flow.osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001194 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1195
1196 /* create a transmitter not yet known by MGW */
1197 tx_hdl := valueof(t_TxHandleAMR590(2));
1198 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1199
1200 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1201 f_sleep(1.0);
1202 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1203
1204 stats := f_osmuxem_stats_get(OsmuxEM);
1205
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001206 if (stats.num_pkts_tx < 40 / flow.osmux.cfg.batch_size) {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001207 setverdict(fail);
1208 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001209 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 +02001210 setverdict(fail);
1211 }
1212
1213 f_osmuxem_stats_err_check(stats);
1214
1215 setverdict(pass);
1216 }
1217
1218 /* Create one connection in loopback mode, test if the Osmux packets are
1219 * actually reflected */
1220 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1221 var RtpFlowData flow;
1222 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1223 var MgcpCallId call_id := '1225'H;
1224 var OsmuxemStats stats;
1225 var OsmuxTxHandle tx_hdl;
1226
1227 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001228 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001229 flow.em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001230 flow.osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001231 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1232
1233 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1234 f_sleep(1.0);
1235
1236 /* Switch off both Tx, wait to receive delayed frames from MGW */
1237 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1238 f_sleep(0.1);
1239 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1240
1241 stats := f_osmuxem_stats_get(OsmuxEM);
1242
1243 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1244 setverdict(fail);
1245 }
1246 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1247 setverdict(fail);
1248 }
1249
1250 f_osmuxem_stats_err_check(stats);
1251
1252 setverdict(pass);
1253 }
1254
1255 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1256 * must match the reception statistics on the other side and vice versa. The
1257 * user may also supply a tolerance value (number of packets) when deviations
1258 * are acceptable */
1259 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1260 var integer plen;
1261
1262 log("stats A: ", a);
1263 log("stats B: ", b);
1264 log("tolerance: ", tolerance, " packets");
1265 log("batch_size: ", batch_size, " packets");
1266
1267 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1268
1269 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1270 return false;
1271 }
1272
1273 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1274 return false;
1275 }
1276
1277 if(a.num_pkts_tx > 0) {
1278 plen := a.bytes_payload_tx / a.num_pkts_tx;
1279 } else {
1280 plen := 0;
1281 }
1282
1283 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1284 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1285 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1286 return false;
1287 }
1288
1289 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) {
1290 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1291 return false;
1292 }
1293
1294 return true;
1295 }
1296
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001297 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1298 charstring local_ip_rtp, charstring remote_ip_rtp,
1299 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001300 var RtpFlowData flow[2];
1301 var RtpemStats stats_rtp;
1302 var OsmuxemStats stats_osmux;
1303 var MgcpResponse resp;
1304 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1305 var MgcpCallId call_id := '1226'H;
1306 var integer tolerance := 0;
1307
1308 f_init(ep, true);
1309
1310 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001311 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001312 flow[0].rtp_cfg := c_RtpemDefaultCfg
1313 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1314 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1315 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);
1316 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1317 /* bind local RTP emulation sockets */
1318 flow[0].em.portnr := 10000;
1319 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1320
1321 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001322 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001323 flow[1].em.portnr := mp_local_osmux_port;
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001324 flow[1].osmux.local_cid := 2;
1325 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001326 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1327
1328 if (bidir) {
1329 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1330 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1331
1332 /* Note: When we test bidirectional we may
1333 * loose packets during switch off because
1334 * both ends are transmitting and we only
1335 * can switch them off one by one. */
1336 tolerance := 3;
1337 } else {
1338 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1339 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1340 }
1341
1342 f_sleep(1.0);
1343
1344 /* Switch off both Tx, wait to receive delayed frames from MGW */
1345 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1346 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1347 f_sleep(0.1);
1348
1349 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1350 f_flow_delete(RTPEM[1]);
1351
1352 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1353 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001354 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 +02001355 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1356 mtc.stop;
1357 }
1358
1359 f_rtpem_stats_err_check(stats_rtp);
1360 f_osmuxem_stats_err_check(stats_osmux);
1361
1362 setverdict(pass);
1363 }
1364
1365 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1366 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001367 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1368 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001369 }
1370
1371 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1372 * exchange some data in both directions */
1373 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001374 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1375 mp_local_ipv4, mp_remote_ipv4);
1376 }
1377
1378 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1379 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1380 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1381 mp_local_ipv6, mp_remote_ipv6);
1382 }
1383 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1384 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1385 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1386 mp_local_ipv6, mp_remote_ipv6);
1387 }
1388 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1389 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1390 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1391 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001392 }
1393
1394
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001395 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1396 charstring local_ip_rtp, charstring remote_ip_rtp,
1397 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001398 var RtpFlowData flow[2];
1399 var RtpemStats stats_rtp;
1400 var OsmuxemStats stats_osmux;
1401 var MgcpResponse resp;
1402 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1403 var MgcpCallId call_id := '1227'H;
1404 var integer num_pkts_tx[2];
1405 var integer temp;
1406
1407 f_init(ep, true);
1408
1409 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001410 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001411 flow[0].rtp_cfg := c_RtpemDefaultCfg
1412 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1413 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1414 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);
1415 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1416 /* bind local RTP emulation sockets */
1417 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001418 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001419
1420
1421 /* Create the second connection. This connection will be also
1422 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001423 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001424 flow[1].em.portnr := mp_local_osmux_port;
1425 if (crcx_osmux_wildcard) {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001426 flow[1].osmux.local_cid := -1;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001427 } else {
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001428 flow[1].osmux.local_cid := 2;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001429 }
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001430 flow[1].osmux.cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001431 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001432
1433
1434 /* The first leg starts transmitting */
1435 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1436 f_sleep(0.5);
1437 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1438 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1439 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1440 mtc.stop;
1441 }
1442 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1443 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1444 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1445 mtc.stop;
1446 }
1447
1448 /* The second leg starts transmitting a little later */
1449 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1450 f_sleep(1.0);
1451 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1452 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1453 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1454 mtc.stop;
1455 }
1456 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1457 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1458 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1459 mtc.stop;
1460 }
1461
1462 /* The first leg will now be switched into bidirectional
1463 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001464 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1465 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1466 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001467 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1468 f_sleep(0.5);
1469 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1470 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1471 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1472 mtc.stop;
1473 }
1474 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1475 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1476 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1477 mtc.stop;
1478 }
1479
1480 /* When the second leg is switched into bidirectional mode
1481 * as well, then the MGW will connect the two together and
1482 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001483 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1484 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001485 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001486
1487 if (crcx_osmux_wildcard) {
1488 /* For now we must set same CID as the MGW recvCID,
1489 * having sendCID!=recvCID is not yet supported. */
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001490 flow[1].osmux.local_cid := flow[1].osmux.remote_cid;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001491 }
1492 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1493 f_sleep(2.0);
1494
1495 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1496 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1497
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001498 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux.cfg.batch_size;
1499 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 +02001500 log("stats_rtp: ", stats_rtp);
1501 log("stats_osmux: ", stats_osmux);
1502 log("old_rtp_tx: ", num_pkts_tx[0]);
1503 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1504 mtc.stop;
1505 }
1506
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001507 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux.cfg.batch_size;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001508 if (temp > 3 or temp < -3) {
Pau Espin Pedroldb1e0682022-10-06 19:40:29 +02001509 log("stats_rtp: ", stats_rtp);
1510 log("stats_osmux: ", stats_osmux);
1511 log("old_osmux_tx: ", num_pkts_tx[1]);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001512 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1513 mtc.stop;
1514 }
1515
1516 f_rtpem_stats_err_check(stats_rtp);
1517 f_osmuxem_stats_err_check(stats_osmux);
1518
1519 /* Tear down */
1520 f_flow_delete(RTPEM[0]);
1521 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1522 setverdict(pass);
1523 }
1524
1525 /* create one RTP and one OSmux emulations and pass data in both
1526 directions. Create CRCX with wildcard Osmux CID and set it later
1527 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1528 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001529 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1530 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001531 }
1532
1533 /* create one RTP and one OSmux emulations and pass data in both
1534 directions. Create CRCX with fixed Osmux CID and keep it during
1535 MDCX. This is similar to how BSC sets up the call in AoIP. */
1536 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001537 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1538 mp_local_ipv4, mp_remote_ipv4);
1539 }
1540
1541 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1542 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1543 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1544 mp_local_ipv6, mp_remote_ipv6);
1545 }
1546 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1547 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1548 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1549 mp_local_ipv6, mp_remote_ipv6);
1550 }
1551 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1552 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1553 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1554 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001555 }
1556
Harald Welte646ecdb2017-12-28 03:21:57 +01001557 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1558 var template MgcpCommand cmd;
1559 var MgcpResponse resp;
1560
1561 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1562 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1563
1564 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1565
1566 setverdict(pass);
1567 }
1568
1569 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1570 var MgcpEndpoint ep;
1571 var MgcpCallId call_id;
1572 var integer ep_nr;
1573
1574 f_init();
1575
1576 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001577 if(ep_nr > 15) {
1578 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1579 } else {
1580 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1581 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001582 call_id := int2hex(ep_nr, 2) & '1234'H;
1583 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1584 }
1585 }
1586
Harald Welte79181ff2017-11-18 19:26:11 +01001587 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1588 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001589 var template MgcpCommand cmd;
1590 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001591 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001592 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001593
Harald Welteedc45c12017-11-18 19:15:05 +01001594 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001595
Harald Welteba62c8c2017-11-18 18:26:49 +01001596 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001597 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001598
Harald Welteba62c8c2017-11-18 18:26:49 +01001599 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001600
1601 setverdict(pass);
1602 }
1603
Harald Welte79181ff2017-11-18 19:26:11 +01001604 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1605 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1606 var template MgcpCommand cmd;
1607 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001608 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001609 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001610
1611 f_init(ep);
1612
1613 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1614 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1615
1616 f_dlcx_ok(ep);
1617
1618 setverdict(pass);
1619 }
1620
1621
Harald Welte6d167f82017-11-18 19:41:35 +01001622 /* CRCX + DLCX of valid endpoint but invalid call-id */
1623 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1624 var template MgcpCommand cmd;
1625 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001626 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001627 var MgcpCallId call_id := '51231'H;
1628
1629 f_init(ep);
1630
1631 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1632 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1633
1634 f_dlcx(ep, "516", *, 'ffff'H);
1635
1636 setverdict(pass);
1637 }
1638
1639
1640 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1641 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1642 var template MgcpCommand cmd;
1643 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001644 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001645 var MgcpCallId call_id := '51230'H;
1646
1647 f_init(ep);
1648
1649 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1650 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1651
1652 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1653
1654 setverdict(pass);
1655 }
1656
1657
Harald Weltee636afd2017-09-17 16:24:09 +08001658 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001659 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1660 var template MgcpCommand cmd;
1661 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001662 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001663 var MgcpCallId call_id := '51229'H;
1664 var template MgcpResponse rtmpl := {
1665 line := {
1666 code := "200",
1667 string := "OK"
1668 },
1669 params:= { },
1670 sdp := omit
1671 };
1672
1673 f_init(ep);
1674
1675 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1676 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1677
1678 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1679 resp := mgcp_transceive_mgw(cmd, rtmpl);
1680 resp := mgcp_transceive_mgw(cmd, rtmpl);
1681
1682 setverdict(pass);
1683 }
1684
Harald Weltebb7523b2018-03-29 08:52:01 +02001685 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1686 charstring codec) := {
1687 em := {
1688 hostname := host_a,
1689 portnr := omit
1690 },
1691 mgw := {
1692 hostname := host_b,
1693 portnr := omit
1694 },
1695 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001696 codec := codec,
Pau Espin Pedrol779e4a72022-10-07 12:44:42 +02001697 osmux:= {
1698 local_cid_sent := false,
1699 local_cid := omit,
1700 remote_cid := omit,
1701 cfg := omit
1702 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001703 }
Harald Weltef53f1642017-11-18 19:57:11 +01001704
Harald Weltebb7523b2018-03-29 08:52:01 +02001705 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1706 testcase TC_rtpem_selftest() runs on dummy_CT {
1707 var RtpemStats stats[2];
1708 var integer local_port := 10000;
1709 var integer local_port2 := 20000;
1710
1711 f_init();
1712
1713 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1714 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1715
1716 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1717 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1718
1719 log("=== starting");
1720 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1721 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1722
1723 f_sleep(5.0);
1724
1725 log("=== stopping");
1726 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1727 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1728 f_sleep(0.5);
1729 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1730 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1731
1732 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1733 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1734 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1735 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001736 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001737 }
1738 setverdict(pass);
1739 }
1740
Philipp Maier2321ef92018-06-27 17:52:04 +02001741 /* Create one half open connection in receive-only mode. The MGW must accept
1742 * the packets but must not send any. */
1743 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1744 var RtpFlowData flow;
1745 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1746 var MgcpCallId call_id := '1225'H;
1747 var RtpemStats stats;
1748
1749 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001750 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001751 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001752 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001753
1754 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1755 f_sleep(1.0);
1756 f_flow_delete(RTPEM[0], ep, call_id);
1757
1758 stats := f_rtpem_stats_get(RTPEM[0]);
1759
Philipp Maier42b17cc2019-10-01 13:53:17 +02001760 /* Make sure that at least some amount of RTP packets/bytes
1761 * have has been transmitted. The compare values for
1762 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1763 * using a testrun and the results were devided by 2, so even
1764 * in load situations we should reach the minimum amount of
1765 * required packets/bytes */
1766
1767 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001768 setverdict(fail);
1769 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001770 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001771 setverdict(fail);
1772 }
Philipp Maier36291392018-07-25 09:40:44 +02001773
1774 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001775
1776 setverdict(pass);
1777 }
1778
1779 /* Create one connection in loopback mode, test if the RTP packets are
1780 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001781 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 +02001782 var RtpFlowData flow;
1783 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1784 var MgcpCallId call_id := '1225'H;
1785 var RtpemStats stats;
1786
1787 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001788 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001789 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001790 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001791
1792 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1793 f_sleep(1.0);
1794 f_flow_delete(RTPEM[0], ep, call_id);
1795
1796 stats := f_rtpem_stats_get(RTPEM[0]);
1797
1798 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1799 setverdict(fail);
1800 }
1801 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1802 setverdict(fail);
1803 }
Philipp Maier36291392018-07-25 09:40:44 +02001804
1805 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001806
1807 setverdict(pass);
1808 }
1809
Philipp Maier1ac13982021-05-07 23:06:07 +02001810 /* Create one connection in loopback mode, test if the RTP packets are
1811 * actually reflected */
1812 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001813 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001814 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001815 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1816 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1817 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001818
1819 /* Same as above, but we will intenionally not tell the MGW where to
1820 * send the outgoing traffic. The connection is still created in
1821 * loopback mode, so the MGW should take the originating address from
1822 * the incoming RTP packet and send it back to the source */
1823 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001824 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001825 }
1826
1827
Philipp Maier7df85f62018-07-25 10:26:09 +02001828 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1829 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001830 var RtpFlowData flow[2];
1831 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001832 var MgcpResponse resp;
1833 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1834 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001835 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001836
1837 f_init(ep);
1838
1839 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001840 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001841 /* bind local RTP emulation sockets */
1842 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001843 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001844
1845 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001846 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001847 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001848 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1849
1850 if (bidir) {
1851 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1852 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1853
1854 /* Note: When we test bidirectional we may
1855 * loose packets during switch off because
1856 * both ends are transmitting and we only
1857 * can switch them off one by one. */
1858 tolerance := 3;
1859 } else {
1860 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1861 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1862 }
1863
1864 f_sleep(1.0);
1865
1866 f_flow_delete(RTPEM[1]);
1867 f_flow_delete(RTPEM[0], ep, call_id);
1868
1869 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1870 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1871 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1872 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001873 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001874 }
1875
Philipp Maier36291392018-07-25 09:40:44 +02001876 f_rtpem_stats_err_check(stats[0]);
1877 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001878
Philipp Maier2321ef92018-06-27 17:52:04 +02001879 setverdict(pass);
1880 }
1881
1882 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1883 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001884 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001885 }
1886
1887 /* create two local RTP emulations; create two connections on MGW EP,
1888 * exchange some data in both directions */
1889 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001890 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1891 }
1892
1893 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1894 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1895 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1896 }
1897
1898 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1899 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1900 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001901 }
1902
1903 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001904 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1905 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001906 var RtpFlowData flow[2];
1907 var RtpemStats stats[2];
1908 var MgcpResponse resp;
1909 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1910 var MgcpCallId call_id := '1227'H;
1911 var integer num_pkts_tx[2];
1912 var integer temp;
1913
1914 f_init(ep);
1915
1916 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001917 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001918 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001919 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001920
1921 /* Create the second connection. This connection will be also
1922 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001923 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001924 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001925 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001926
1927 /* The first leg starts transmitting */
1928 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1929 f_sleep(0.5);
1930 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1931 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001932 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001933 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001934 }
1935 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1936 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001937 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001938 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001939 }
1940
1941 /* The second leg starts transmitting a little later */
1942 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1943 f_sleep(1.0);
1944 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1945 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001946 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001947 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001948 }
1949 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1950 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001951 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001952 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001953 }
1954
1955 /* The first leg will now be switched into bidirectional
1956 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001957 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1958 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1959 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001960 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1961 f_sleep(0.5);
1962 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001963 if (stats[0].num_pkts_rx_err_disabled != 0) {
1964 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001965 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001966 }
1967 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1968 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001969 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001970 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001971 }
1972
1973 /* When the second leg is switched into bidirectional mode
1974 * as well, then the MGW will connect the two together and
1975 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02001976 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1977 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001978 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001979 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1980 f_sleep(2.0);
1981
1982 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1983 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1984
1985 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1986 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001987 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001988 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001989 }
1990
1991 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1992 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001993 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001994 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001995 }
Philipp Maier36291392018-07-25 09:40:44 +02001996
1997 f_rtpem_stats_err_check(stats[0]);
1998 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001999
2000 /* Tear down */
2001 f_flow_delete(RTPEM[0]);
2002 f_flow_delete(RTPEM[1], ep, call_id);
2003 setverdict(pass);
2004 }
2005
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002006 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
2007 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2008 mp_local_ipv4, mp_remote_ipv4);
2009 }
2010
2011 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
2012 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
2013 mp_local_ipv6, mp_remote_ipv6);
2014 }
2015
2016 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
2017 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2018 mp_local_ipv6, mp_remote_ipv6);
2019 }
2020
Philipp Maier2321ef92018-06-27 17:52:04 +02002021 /* Test what happens when two RTP streams from different sources target
2022 * a single connection. Is the unsolicited stream properly ignored? */
2023 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2024 var RtpFlowData flow[2];
2025 var RtpemStats stats[2];
2026 var MgcpResponse resp;
2027 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2028 var MgcpCallId call_id := '1234321326'H;
2029 var integer unsolicited_port := 10002;
2030
2031 f_init(ep);
2032
2033 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002034 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002035 /* bind local RTP emulation sockets */
2036 flow[0].em.portnr := 10000;
2037 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2038
2039 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002040 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002041 flow[1].em.portnr := 20000;
2042 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002043
2044 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2045 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2046
Philipp Maier2321ef92018-06-27 17:52:04 +02002047 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002048
Philipp Maier2321ef92018-06-27 17:52:04 +02002049 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002050 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2051 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002052 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2053
2054 f_sleep(0.5);
2055
Daniel Willmanna069d382018-12-13 13:53:33 +01002056 /* Stop transmitting packets and tear down the flows */
2057 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002058 f_flow_delete(RTPEM[0]);
2059 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002060
2061 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2062 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2063 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2064 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002065 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002066 }
2067
Philipp Maier36291392018-07-25 09:40:44 +02002068 f_rtpem_stats_err_check(stats[0]);
2069 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002070
Harald Weltebb7523b2018-03-29 08:52:01 +02002071 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002072 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002073
Philipp Maier2321ef92018-06-27 17:52:04 +02002074 /* Test a handover situation. We first create two connections transmit
2075 * some data bidirectionally. Then we will simulate a handover situation. */
2076 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2077 var RtpFlowData flow[2];
2078 var RtpemStats stats[3];
2079 var MgcpResponse resp;
2080 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2081 var MgcpCallId call_id := '76338'H;
2082 var integer port_old;
2083
2084 f_init(ep);
2085
2086 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002087 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002088 /* bind local RTP emulation sockets */
2089 flow[0].em.portnr := 10000;
2090 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2091
2092 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002093 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002094 flow[1].em.portnr := 20000;
2095 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2096
2097 /* Normal rtp flow for one second */
2098 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2099 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2100 f_sleep(1.0);
2101
2102 /* Now switch the flow over to a new port (BTS) */
2103 port_old := flow[0].em.portnr;
2104 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002105 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002106 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002107 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002108
2109 /* When handing over a call, the old source may still keep
2110 * transmitting for a while. We simulate this by injecting
2111 * some unsolicited packets on the behalf of the old source,
2112 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002113 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2114 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002115 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2116 f_sleep(1.0);
2117 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2118 f_sleep(1.0);
2119
2120 /* Terminate call */
2121 f_flow_delete(RTPEM[0]);
2122 f_flow_delete(RTPEM[1], ep, call_id);
2123
2124 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2125 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2126 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2127 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002128 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002129 }
2130 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2131 if (stats[2].num_pkts_rx_err_disabled != 0) {
2132 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002133 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002134 }
2135
Philipp Maier36291392018-07-25 09:40:44 +02002136 f_rtpem_stats_err_check(stats[0]);
2137 f_rtpem_stats_err_check(stats[1]);
2138 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002139
Philipp Maier2321ef92018-06-27 17:52:04 +02002140 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002141 }
Harald Weltef53f1642017-11-18 19:57:11 +01002142
Philipp Maier6d4e0942019-02-21 17:35:01 +01002143
2144 /* create two local RTP emulations; create two connections on MGW EP, see if
2145 * exchanged data is converted bwtween ts101318 and rfc5993 */
2146 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2147 var RtpFlowData flow[2];
2148 var RtpemStats stats[2];
2149 var MgcpResponse resp;
2150 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2151 var MgcpCallId call_id := '1226'H;
2152
2153 f_init(ep);
2154
2155 /* Turn on conversion mode */
2156 f_vty_enter_config(MGWVTY);
2157 f_vty_transceive(MGWVTY, "mgcp");
2158 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2159
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002160 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002161 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002162 /* bind local RTP emulation sockets */
2163 flow[0].em.portnr := 10000;
2164 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2165 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2166 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2167 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2168 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2169
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002170 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002171 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002172 flow[1].em.portnr := 20000;
2173 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2174 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2175 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2176 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2177 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2178
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002179 /* Send RTP packets to connection #0, receive on connection #1 */
2180 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2181 f_sleep(0.5);
2182 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002183 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002184 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2185 f_sleep(0.5);
2186 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002187
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002188 /* Send RTP packets to connection #1, receive on connection #0 */
2189 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2190 f_sleep(0.5);
2191 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2192 f_sleep(1.0);
2193 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2194 f_sleep(0.5);
2195 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2196
2197 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002198 f_flow_delete(RTPEM[0]);
2199 f_flow_delete(RTPEM[1], ep, call_id);
2200
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002201 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002202 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2203 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002204 f_rtpem_stats_err_check(stats[0]);
2205 f_rtpem_stats_err_check(stats[1]);
2206
2207 /* Turn off conversion mode */
2208 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2209
2210 setverdict(pass);
2211 }
2212
Philipp Maier4f764ce2019-03-07 10:54:10 +01002213 /* create two local RTP emulations; create two connections on MGW EP, see if
2214 * exchanged data is converted between AMR octet-aligned and bandwith
2215 * efficient-mode */
2216 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2217 var RtpFlowData flow[2];
2218 var RtpemStats stats[2];
2219 var MgcpResponse resp;
2220 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2221 var MgcpCallId call_id := '1226'H;
2222
2223 f_init(ep);
2224
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002225 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002226 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002227 /* bind local RTP emulation sockets */
2228 flow[0].em.portnr := 10000;
2229 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2230 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2231 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2232 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2233 flow[0].fmtp := fmtp0;
2234 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2235
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002236 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002237 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002238 flow[1].em.portnr := 20000;
2239 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2240 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2241 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2242 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2243 flow[1].fmtp := fmtp1;
2244 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2245
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002246 /* Send RTP packets to connection #0, receive on connection #1 */
2247 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2248 f_sleep(0.5);
2249 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002250 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002251 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2252 f_sleep(0.5);
2253 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002254
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002255 /* Send RTP packets to connection #1, receive on connection #0 */
2256 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2257 f_sleep(0.5);
2258 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2259 f_sleep(1.0);
2260 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2261 f_sleep(0.5);
2262 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2263
2264 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002265 f_flow_delete(RTPEM[0]);
2266 f_flow_delete(RTPEM[1], ep, call_id);
2267
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002268 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002269 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2270 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002271 f_rtpem_stats_err_check(stats[0]);
2272 f_rtpem_stats_err_check(stats[1]);
2273
2274 setverdict(pass);
2275 }
2276
Philipp Maier882843d2020-05-25 15:33:13 +02002277 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2278 * functions are real world AMR RTP payloads including AMR header. The
2279 * payloads were extracted from a trace with known good payloads. */
2280
Philipp Maier4f764ce2019-03-07 10:54:10 +01002281 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002282 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002283 }
2284
2285 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2286 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2287 }
2288
2289 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2290 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2291 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002292
Harald Weltee636afd2017-09-17 16:24:09 +08002293 /* TODO: Double-DLCX (no retransmission) */
2294
2295
2296
2297 /* TODO: AUEP (various) */
2298 /* TODO: RSIP (various) */
2299 /* TODO: RQNT (various) */
2300 /* TODO: EPCF (various) */
2301 /* TODO: AUCX (various) */
2302 /* TODO: invalid verb (various) */
2303
Oliver Smith021141e2019-06-25 12:09:01 +02002304
2305 testcase TC_conn_timeout() runs on dummy_CT {
2306 var RtpFlowData flow;
2307 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2308 var MgcpCallId call_id := '1225'H;
2309 var MGCP_RecvFrom mrf;
2310
2311 f_init(ep);
2312 log("Setting conn-timeout to 1s");
2313 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2314
2315 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002316 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002317 flow.em.portnr := 10000;
2318 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2319 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2320 f_sleep(1.5);
2321
2322 log("Stopping for 0.5s and resuming");
2323 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2324 f_sleep(0.5);
2325 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2326 f_sleep(0.1);
2327
2328 log("Stopping for 1.5s, expecting to run into timeout");
2329 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2330 f_sleep(1.5);
2331
2332 log("Resuming should fail now");
2333 f_rtpem_conn_refuse_expect(RTPEM[0]);
2334 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2335 f_sleep(0.2);
2336 f_rtpem_conn_refuse_verify(RTPEM[0]);
2337
2338 setverdict(pass);
2339 }
2340
Philipp Maier2609c752020-07-08 12:38:09 +02002341 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2342 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2343 var template MgcpCommand cmd;
2344 var MgcpResponse resp;
2345 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2346 var MgcpCallId call_id := '8376F297'H;
2347
2348 f_init(ep);
2349
2350 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2351 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2352
2353 f_dlcx_ok(ep);
2354
2355 setverdict(pass);
2356 }
2357
2358 /* Test what happens when overlapping endpoints are selected (E1) */
2359 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2360 var template MgcpCommand cmd;
2361 var MgcpResponse resp;
2362 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2363 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2364 var MgcpCallId call_id_1 := '8376F297'H;
2365 var MgcpCallId call_id_2 := '837AF2A7'H;
2366
2367 f_init();
2368
2369 /* ep_1 and ep_2 are overlapping, selecting both one after
2370 * another should work fine: */
2371 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2372 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2373 f_dlcx_ok(ep_1);
2374 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2375 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2376 f_dlcx_ok(ep_2);
2377
2378 /* When ep_1 is serving a call we can not select ep_2 becaus
2379 * it is overlapping with ep_1 */
2380 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2381 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2382 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2383 resp := mgcp_transceive_mgw(cmd, ?);
2384 if (resp.line.code != "501") {
2385 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2386 }
2387 f_dlcx_ok(ep_1);
2388
2389 setverdict(pass);
2390 }
2391
2392 /* Create one connection in loopback mode, test if the RTP packets are
2393 * actually reflected */
2394 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2395 var RtpFlowData flow;
2396 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2397 var MgcpCallId call_id := '12250989'H;
2398 var RtpemStats stats;
2399
2400 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002401 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002402 flow.em.portnr := 10000;
2403 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2404
2405 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2406 f_sleep(1.0);
2407 f_flow_delete(RTPEM[0], ep, call_id);
2408
2409 stats := f_rtpem_stats_get(RTPEM[0]);
2410
2411 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2412 setverdict(fail);
2413 }
2414 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2415 setverdict(fail);
2416 }
2417
2418 f_rtpem_stats_err_check(stats);
2419
2420 setverdict(pass);
2421 }
2422
Philipp Maier13aff992022-06-30 16:19:59 +02002423 /* test valid wildcarded DLCX on an E1 trunk */
2424 testcase TC_e1_dlcx_wildcarded() runs on dummy_CT {
2425 var template MgcpCommand cmd;
2426 var MgcpEndpoint ep;
2427 var MgcpCallId call_id := '8376F297'H;
2428 var integer n_e1_ts := 4;
2429 var StatsDExpects expect;
2430
2431 f_init();
2432
2433 /* Open a few E1 timeslots */
2434 for (var integer i := 0; i < n_e1_ts; i := i+1) {
2435 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-0@" & c_mgw_domain;
2436 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2437 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2438 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-2@" & c_mgw_domain;
2439 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2440 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2441 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-4@" & c_mgw_domain;
2442 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2443 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2444 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-6@" & c_mgw_domain;
2445 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2446 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2447 }
2448
2449 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
2450 * occupied endpoints */
2451 f_sleep(1.0)
2452 expect := {
2453 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := n_e1_ts * 4, max := n_e1_ts * 4}
2454 };
2455 f_statsd_expect(expect);
2456
2457 /* Send wildcarded DLCX */
2458 var template MgcpResponse rtmpl := {
2459 line := {
2460 code := "200",
2461 string := ?
2462 },
2463 params:= { },
2464 sdp := omit
2465 };
2466 ep := "ds/e1-1/*@" & c_mgw_domain;
2467 cmd := ts_DLCX(get_next_trans_id(), ep);
2468 mgcp_transceive_mgw(cmd, rtmpl);
2469
2470 /* Query a the statsd once to ensure that intermediate results are pulled from the
2471 * pipeline. The second query (below) will return the actual result. */
2472 expect := {
2473 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := n_e1_ts * 4}
2474 };
2475 f_statsd_expect(expect);
2476
2477 /* The second query must resturn a result with 0 endpoints in use. */
2478 expect := {
2479 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := 0}
2480 };
2481 f_statsd_expect(expect);
2482
2483 setverdict(pass);
2484 }
2485
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002486 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2487 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2488 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2489 var template MgcpCommand cmd;
2490 var MgcpResponse resp;
2491 var MgcpCallId call_id := '1234'H;
2492 var MgcpConnectionId conn_id;
2493
2494 f_init(ep);
2495
2496 /* create the connection on the MGW */
2497 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2498 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2499 conn_id := extract_conn_id(resp);
2500
2501 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2502 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2503 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2504 valueof(ts_SDP_ptime(20)) });
2505 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2506
2507 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2508 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2509 }
2510 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2511 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2512 }
2513
2514 /* clean-up */
2515 f_dlcx_ok(ep, call_id);
2516 setverdict(pass);
2517 }
2518
2519 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2520 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2521 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2522 var template MgcpCommand cmd;
2523 var MgcpResponse resp;
2524 var MgcpCallId call_id := '1234'H;
2525 var MgcpConnectionId conn_id;
2526
2527 f_init(ep);
2528
2529 /* create the connection on the MGW */
2530 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2531 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2532 conn_id := extract_conn_id(resp);
2533
2534 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2535 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2536 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2537 valueof(ts_SDP_ptime(20)) });
2538 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2539
2540 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2541 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2542 }
2543 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2544 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2545 }
2546
2547 /* clean-up */
2548 f_dlcx_ok(ep, call_id);
2549 setverdict(pass);
2550 }
2551
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002552 /* create two local RTP+IuUP emulations and pass data in both directions */
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002553 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002554 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002555 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2556 var RtpFlowData flow[2];
2557 var RtpemStats stats[2];
2558 var MgcpResponse resp;
2559 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2560 var MgcpCallId call_id := '1227'H;
2561 var integer num_pkts_tx[2];
2562 var integer temp;
2563
2564 f_init(ep);
2565
2566 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2567 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2568 flow[0].em.portnr := 10000;
2569 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2570 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2571 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002572 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002573 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002574 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2575 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2576
2577 /* Create the second connection. This connection will be also
2578 * in receive only mode (CN side, IuUP-Init passive) */
2579 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2580 flow[1].em.portnr := 20000;
2581 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2582 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2583 flow[1].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002584 flow[1].rtp_cfg.iuup_cfg.active_init := false;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002585 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2586 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2587
2588 /* The first leg starts transmitting */
2589 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2590 f_sleep(0.5);
2591 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2592 if (stats[0].num_pkts_rx_err_disabled != 0) {
2593 setverdict(fail, "received packets from MGW on recvonly connection 0");
2594 mtc.stop;
2595 }
2596 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2597 if (stats[1].num_pkts_rx_err_disabled != 0) {
2598 setverdict(fail, "received packets from MGW on recvonly connection 1");
2599 mtc.stop;
2600 }
2601
2602 /* The second leg starts transmitting a little later */
2603 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2604 f_sleep(1.0);
2605 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2606 if (stats[0].num_pkts_rx_err_disabled != 0) {
2607 setverdict(fail, "received packets from MGW on recvonly connection 0");
2608 mtc.stop;
2609 }
2610 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2611 if (stats[1].num_pkts_rx_err_disabled != 0) {
2612 setverdict(fail, "received packets from MGW on recvonly connection 1");
2613 mtc.stop;
2614 }
2615
2616 /* The first leg will now be switched into bidirectional
2617 * mode, but we do not expect any data coming back yet. */
2618 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2619 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2620 num_pkts_tx[1] := stats[1].num_pkts_tx;
2621 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2622 f_sleep(0.5);
2623 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2624 if (stats[0].num_pkts_rx_err_disabled != 0) {
2625 setverdict(fail, "received packets from MGW on recvonly connection 0");
2626 mtc.stop;
2627 }
2628 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2629 if (stats[1].num_pkts_rx_err_disabled != 0) {
2630 setverdict(fail, "received packets from MGW on recvonly connection 1");
2631 mtc.stop;
2632 }
2633
2634 /* When the second leg is switched into bidirectional mode
2635 * as well, then the MGW will connect the two together and
2636 * we should see RTP streams passing through from both ends. */
2637 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2638 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2639 num_pkts_tx[0] := stats[0].num_pkts_tx;
2640 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2641 f_sleep(2.0);
2642
2643 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2644 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2645
2646 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2647 if (temp > 3 or temp < -3) {
2648 setverdict(fail, "number of packets not within normal parameters:", temp);
2649 mtc.stop;
2650 }
2651
2652 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2653 if (temp > 3 or temp < -3) {
2654 setverdict(fail, "number of packets not within normal parameters:", temp);
2655 mtc.stop;
2656 }
2657
2658 f_rtpem_stats_err_check(stats[0]);
2659 f_rtpem_stats_err_check(stats[1]);
2660
2661 /* Tear down */
2662 f_flow_delete(RTPEM[0]);
2663 f_flow_delete(RTPEM[1], ep, call_id);
2664 setverdict(pass);
2665 }
2666 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002667 var template (value) IuUP_RabFlowCombinationList rfcl := {
2668 t_IuUP_RFC_AMR_12_2(0),
2669 t_IuUP_RFC_AMR_SID(1),
2670 t_IuUP_RFC_AMR_NO_DATA(2)
2671 };
2672 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2673 mp_local_ipv4, mp_remote_ipv4);
2674 }
2675 /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */
2676 testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT {
2677 var template (value) IuUP_RabFlowCombinationList rfcl := {
2678 t_IuUP_RFC_AMR_12_2(1),
2679 t_IuUP_RFC_AMR_SID(2),
2680 t_IuUP_RFC_AMR_NO_DATA(0)
2681 };
2682 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002683 mp_local_ipv4, mp_remote_ipv4);
2684 }
2685
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002686 /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */
2687 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 +02002688 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002689 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2690 var RtpFlowData flow[2];
2691 var RtpemStats stats[2];
2692 var MgcpResponse resp;
2693 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2694 var MgcpCallId call_id := '1227'H;
2695 var integer num_pkts_tx[2];
2696 var integer temp;
2697
2698 f_init(ep);
2699
2700 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2701 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2702 flow[0].em.portnr := 10000;
2703 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2704 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2705 flow[0].rtp_cfg.tx_fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
2706 flow[0].rtp_cfg.rx_fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O; /* flow[1].rtp_cfg.tx_fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
2707 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002708 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002709 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002710 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2711 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2712
2713 /* Create the second connection. This connection will be also
2714 * in receive only mode (CN side, regular RTP) */
2715 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
2716 flow[1].em.portnr := 20000;
2717 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2718 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2719 flow[1].rtp_cfg.tx_fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
2720 flow[1].rtp_cfg.rx_fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O; /* flow[0].rtp_cfg.tx_fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
2721 flow[1].rtp_cfg.iuup_mode := false;
2722 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2723 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2724
2725 /* The first leg starts transmitting */
2726 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2727 f_sleep(0.5);
2728 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2729 if (stats[0].num_pkts_rx_err_disabled != 0) {
2730 setverdict(fail, "received packets from MGW on recvonly connection 0");
2731 mtc.stop;
2732 }
2733 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2734 if (stats[1].num_pkts_rx_err_disabled != 0) {
2735 setverdict(fail, "received packets from MGW on recvonly connection 1");
2736 mtc.stop;
2737 }
2738
2739 /* The second leg starts transmitting a little later */
2740 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2741 f_sleep(1.0);
2742 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2743 if (stats[0].num_pkts_rx_err_disabled != 0) {
2744 setverdict(fail, "received packets from MGW on recvonly connection 0");
2745 mtc.stop;
2746 }
2747 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2748 if (stats[1].num_pkts_rx_err_disabled != 0) {
2749 setverdict(fail, "received packets from MGW on recvonly connection 1");
2750 mtc.stop;
2751 }
2752
2753 /* The first leg will now be switched into bidirectional
2754 * mode, but we do not expect any data coming back yet. */
2755 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2756 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2757 num_pkts_tx[1] := stats[1].num_pkts_tx;
2758 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2759 f_sleep(0.5);
2760 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2761 if (stats[0].num_pkts_rx_err_disabled != 0) {
2762 setverdict(fail, "received packets from MGW on recvonly connection 0");
2763 mtc.stop;
2764 }
2765 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2766 if (stats[1].num_pkts_rx_err_disabled != 0) {
2767 setverdict(fail, "received packets from MGW on recvonly connection 1");
2768 mtc.stop;
2769 }
2770
2771 /* When the second leg is switched into bidirectional mode
2772 * as well, then the MGW will connect the two together and
2773 * we should see RTP streams passing through from both ends. */
2774 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2775 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2776 num_pkts_tx[0] := stats[0].num_pkts_tx;
2777 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2778 f_sleep(2.0);
2779
2780 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2781 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2782
2783 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2784 if (temp > 3 or temp < -3) {
2785 setverdict(fail, "number of packets not within normal parameters:", temp);
2786 mtc.stop;
2787 }
2788
2789 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2790 if (temp > 3 or temp < -3) {
2791 setverdict(fail, "number of packets not within normal parameters:", temp);
2792 mtc.stop;
2793 }
2794
2795 f_rtpem_stats_err_check(stats[0]);
2796 f_rtpem_stats_err_check(stats[1]);
2797
2798 /* Tear down */
2799 f_flow_delete(RTPEM[0]);
2800 f_flow_delete(RTPEM[1], ep, call_id);
2801 setverdict(pass);
2802 }
2803 testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002804 var template (value) IuUP_RabFlowCombinationList rfcl := {
2805 t_IuUP_RFC_AMR_12_2(0),
2806 t_IuUP_RFC_AMR_SID(1),
2807 t_IuUP_RFC_AMR_NO_DATA(2)
2808 };
2809 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2810 mp_local_ipv4, mp_remote_ipv4);
2811 }
2812 /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */
2813 testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT {
2814 var template (value) IuUP_RabFlowCombinationList rfcl := {
2815 t_IuUP_RFC_AMR_12_2(1),
2816 t_IuUP_RFC_AMR_SID(2),
2817 t_IuUP_RFC_AMR_NO_DATA(0)
2818 };
2819 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002820 mp_local_ipv4, mp_remote_ipv4);
2821 }
2822
Harald Welte00a067f2017-09-13 23:27:17 +02002823 control {
2824 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002825 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002826 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002827 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002828 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002829 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002830 execute(TC_crcx_early_bidir_mode());
2831 execute(TC_crcx_unsupp_param());
2832 execute(TC_crcx_missing_callid());
2833 execute(TC_crcx_missing_mode());
2834 execute(TC_crcx_unsupp_packet_intv());
2835 execute(TC_crcx_illegal_double_lco());
2836 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002837 execute(TC_crcx_wildcarded());
2838 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002839 execute(TC_mdcx_without_crcx());
2840 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02002841 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002842 execute(TC_mdcx_wildcarded());
2843 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002844 execute(TC_crcx_and_dlcx_ep_callid_connid());
2845 execute(TC_crcx_and_dlcx_ep_callid());
2846 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002847 execute(TC_crcx_and_dlcx_ep_callid_inval());
2848 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002849 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002850
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002851 execute(TC_crcx_osmux_wildcard());
2852 execute(TC_crcx_osmux_fixed());
2853 execute(TC_crcx_osmux_fixed_twice());
2854 execute(TC_one_crcx_receive_only_osmux());
2855 execute(TC_one_crcx_loopback_osmux());
2856 execute(TC_two_crcx_and_rtp_osmux());
2857 execute(TC_two_crcx_and_rtp_osmux_bidir());
2858 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2859 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2860
Harald Welte33d82162017-12-28 03:21:57 +01002861 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002862
2863 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002864
2865 execute(TC_one_crcx_receive_only_rtp());
2866 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002867 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02002868 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002869 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002870 execute(TC_two_crcx_diff_pt_and_rtp());
2871 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002872 execute(TC_two_crcx_mdcx_and_rtp());
2873 execute(TC_two_crcx_and_unsolicited_rtp());
2874 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002875 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002876 execute(TC_amr_oa_bwe_rtp_conversion());
2877 execute(TC_amr_oa_oa_rtp_conversion());
2878 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002879
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002880 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002881
2882 execute(TC_e1_crcx_and_dlcx_ep());
2883 execute(TC_e1_crcx_with_overlap());
2884 execute(TC_e1_crcx_loopback());
Philipp Maier13aff992022-06-30 16:19:59 +02002885 execute(TC_e1_dlcx_wildcarded());
Philipp Maier2609c752020-07-08 12:38:09 +02002886
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002887 execute(TC_crcx_mdcx_ip4());
2888 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002889 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2890 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2891 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2892 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2893 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2894 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2895 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2896 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02002897
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002898 execute(TC_two_crcx_mdcx_and_iuup());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002899 execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered());
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002900 execute(TC_two_crcx_mdcx_and_iuup_rtp());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002901 execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered());
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002902
Philipp Maier37965082021-05-25 16:44:25 +02002903 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
2904 * older versions of osmo-mgw. This eventually leads into
2905 * a failure of all subsequent testcases, so it is important
2906 * not to add new testcaes after this one. */
2907 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02002908 }
2909}