blob: b0891f5135fe1d9124b720c17c7b6bf63237d490 [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 Pedrolb2c6b382019-05-14 13:40:49 +020022 import from OSMUX_Types all;
23 import from OSMUX_CodecPort all;
24 import from OSMUX_CodecPort_CtrlFunct all;
25 import from OSMUX_Emulation all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080026 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010027 import from General_Types all;
28 import from Native_Functions all;
29 import from IPCP_Types all;
30 import from IP_Types all;
31 import from Osmocom_VTY_Functions all;
32 import from TELNETasp_PortType all;
Philipp Maier55b90542021-07-02 12:33:19 +020033 import from StatsD_Types all;
34 import from StatsD_CodecPort all;
35 import from StatsD_CodecPort_CtrlFunct all;
36 import from StatsD_Checker all;
Harald Welte00a067f2017-09-13 23:27:17 +020037
Philipp Maierbb7a01c2018-02-01 12:32:57 +010038 const charstring c_mgw_domain := "mgw";
39 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
40
Harald Welte21ba5572017-09-19 17:55:05 +080041 /* any variables declared in the component will be available to
42 * all functions that 'run on' the named component, similar to
43 * class members in C++ */
Philipp Maier55b90542021-07-02 12:33:19 +020044 type component dummy_CT extends StatsD_ConnHdlr {
Harald Welte3c6ebb92017-09-16 00:56:57 +080045 port MGCP_CODEC_PT MGCP;
46 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010047 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080048 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010049
Philipp Maier2321ef92018-06-27 17:52:04 +020050 var RTP_Emulation_CT vc_RTPEM[3];
51 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010052
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020053 var OSMUX_Emulation_CT vc_OsmuxEM;
54 port OsmuxEM_CTRL_PT OsmuxEM;
55
Philipp Maier6137c322019-02-20 16:13:41 +010056 port TELNETasp_PT MGWVTY;
Philipp Maier55b90542021-07-02 12:33:19 +020057
58 var StatsD_Checker_CT vc_STATSD;
Harald Welte00a067f2017-09-13 23:27:17 +020059 };
60
Harald Weltee1e18c52017-09-17 16:23:07 +080061 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
62 var MgcpTransId tid := int2str(g_trans_id);
63 g_trans_id := g_trans_id + 1;
64 return tid;
65 }
66
Harald Welte21ba5572017-09-19 17:55:05 +080067 /* all parameters declared here can be modified / overridden by
68 * the config file in the [MODULE_PARAMETERS] section. If no
69 * config file is used or the file doesn't specify them, the
70 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080071 modulepar {
72 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020073 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020074 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080075 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020076 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020077 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010078 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020079 PortNumber mp_local_osmux_port := 1985;
Philipp Maier55b90542021-07-02 12:33:19 +020080 PortNumber mp_mgw_statsd_port := 8125;
Harald Welte3c6ebb92017-09-16 00:56:57 +080081 }
82
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020083 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
84 /* Turn on conversion mode */
85 f_vty_enter_config(MGWVTY);
86 f_vty_transceive(MGWVTY, "mgcp");
87 if (osmux_on) {
88 f_vty_transceive(MGWVTY, "osmux on");
89 } else {
90 f_vty_transceive(MGWVTY, "osmux off");
91 }
92 f_vty_transceive(MGWVTY, "exit");
93 f_vty_transceive(MGWVTY, "exit");
94
95 }
96
97 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +010098 map(self:MGWVTY, system:MGWVTY);
99 f_vty_set_prompts(MGWVTY);
100 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200101
102 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +0100103 }
104
Harald Weltebb7523b2018-03-29 08:52:01 +0200105 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
106 runs on dummy_CT {
107 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
108 map(comp_ref:RTP, system:RTP);
109 map(comp_ref:RTCP, system:RTCP);
110 comp_ref.start(RTP_Emulation.f_main());
111 }
112
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200113 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
114 runs on dummy_CT {
115 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
116 map(comp_ref:OSMUX, system:OSMUX);
117 comp_ref.start(OSMUX_Emulation.f_main());
118 }
119
Harald Welte21ba5572017-09-19 17:55:05 +0800120 /* initialization function, called by each test case at the
121 * beginning, but 'initialized' variable ensures its body is
122 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200123 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800124 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100125 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200126
Harald Welteedc45c12017-11-18 19:15:05 +0100127 if (initialized == false) {
128 initialized := true;
129
130 /* some random number for the initial transaction id */
131 g_trans_id := float2int(rnd()*65535.0);
132 map(self:MGCP, system:MGCP_CODEC_PT);
133 /* connect the MGCP test port using the given
134 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
135 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200136 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 +0200137 if (not ispresent(res.connId)) {
138 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200139 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200140 }
Harald Welteedc45c12017-11-18 19:15:05 +0100141 g_mgcp_conn_id := res.connId;
142
Harald Weltebb7523b2018-03-29 08:52:01 +0200143 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
144 f_rtpem_init(vc_RTPEM[i], i);
145 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
146 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200147 if (osmux_on) {
148 f_osmuxem_init(vc_OsmuxEM);
149 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
150 }
Philipp Maier55b90542021-07-02 12:33:19 +0200151
Philipp Maier2ff3e662021-08-19 10:52:33 +0200152 f_init_statsd("VirtCallAgent", vc_STATSD, mp_local_ipv4, mp_mgw_statsd_port);
Philipp Maier55b90542021-07-02 12:33:19 +0200153 connect(self:STATSD_PROC, vc_STATSD:STATSD_PROC);
154
155 f_statsd_reset();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800156 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800157
Harald Welteedc45c12017-11-18 19:15:05 +0100158 if (isvalue(ep)) {
159 /* do a DLCX on all connections of the EP */
160 f_dlcx_ignore(valueof(ep));
161 }
Philipp Maier6137c322019-02-20 16:13:41 +0100162
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200163 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800164 }
165
Harald Welte00a067f2017-09-13 23:27:17 +0200166 testcase TC_selftest() runs on dummy_CT {
167 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 +0100168 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 +0200169 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
170 "I: 1\n" &
171 "\n" &
172 "v=0\r\n" &
173 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
174 "s=-\r\n" &
175 "c=IN IP4 0.0.0.0\r\n" &
176 "t=0 0\r\n" &
177 "m=audio 0 RTP/AVP 126\r\n" &
178 "a=rtpmap:126 AMR/8000\r\n" &
179 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100180 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 +0200181 "M: sendrecv\r" &
182 "C: 2\r\n" &
183 "I: 1\r\n" &
184 "L: p:20, a:AMR, nt:IN\r\n" &
185 "\n" &
186 "v=0\r\n" &
187 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800188 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200189 "c=IN IP4 0.0.0.0\r\n" &
190 "t=0 0\r\n" &
191 "m=audio 4441 RTP/AVP 99\r\n" &
192 "a=rtpmap:99 AMR/8000\r\n" &
193 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800194 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200195
196 log(c_auep);
197 log(dec_MgcpCommand(c_auep));
198
199 log(c_mdcx3);
200 log(dec_MgcpCommand(c_mdcx3));
201
202 log(c_mdcx3_ret);
203 log(dec_MgcpResponse(c_mdcx3_ret));
204
205 log(c_mdcx4);
206 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800207
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100208 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
209 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 +0800210
211 log(c_crcx510_ret);
212 log(dec_MgcpResponse(c_crcx510_ret));
213 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100214
215 /* We didn't encounter any DTE, so pass the test */
216 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800217 }
218
Harald Weltee636afd2017-09-17 16:24:09 +0800219 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100220 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800221 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
222 * - CRCX with remote session description and without
223 *
224 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100225 * x packetization != 20ms
226 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800227 * x unsupported mode (517)
228 * x bidirectional mode before RemoteConnDesc: 527
229 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100230 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800231 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
232 */
233
Harald Welte21ba5572017-09-19 17:55:05 +0800234 /* build a receive template for receiving a MGCP message. You
235 * pass the MGCP response template in, and it will generate an
236 * MGCP_RecvFrom template that can match the primitives arriving on the
237 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800238 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
239 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100240 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200241 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800242 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200243 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800244 locPort := mp_local_udp_port,
245 msg := { response := resp }
246 }
247 return mrf;
248 }
249
250 /* Send a MGCP request + receive a (matching!) response */
251 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
252 var MgcpMessage msg := { command := valueof(cmd) };
253 resp.line.trans_id := cmd.line.trans_id;
254 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800255 var MGCP_RecvFrom mrf;
256 timer T := 5.0;
257
Harald Welte55015362017-11-18 16:02:42 +0100258 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800259 T.start;
260 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800261 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200262 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
263 setverdict(fail, "Response didn't match template");
264 mtc.stop;
265 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800266 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200267 [] T.timeout {
268 setverdict(fail, "Timeout waiting for response to ", cmd);
269 mtc.stop;
270 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800271 }
272 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800273
274 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
275 return mrf.msg.response;
276 } else {
277 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
278 return r;
279 }
Harald Welte00a067f2017-09-13 23:27:17 +0200280 }
281
Harald Welteba62c8c2017-11-18 18:26:49 +0100282 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
283 var integer i;
284 for (i := 0; i < lengthof(resp.params); i := i + 1) {
285 var MgcpParameter par := resp.params[i];
286 if (par.code == "I") {
287 return str2hex(par.val);
288 }
289 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200290 setverdict(fail, "Could not find conn id for MgcpReponse");
291 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100292 return '00000000'H;
293 }
294
Harald Welte10889c12017-11-18 19:40:31 +0100295 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
296 template MgcpCallId call_id := omit,
297 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100298 var template MgcpCommand cmd;
299 var MgcpResponse resp;
300 var template MgcpResponse rtmpl := {
301 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100302 code := ret_code,
303 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100304 },
305 params := *,
306 sdp := *
307 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100308 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100309 resp := mgcp_transceive_mgw(cmd, rtmpl);
310 }
311
Harald Welte10889c12017-11-18 19:40:31 +0100312 /* Send DLCX and expect OK response */
313 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
314 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100315 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100316 }
317
Harald Welteba62c8c2017-11-18 18:26:49 +0100318 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100319 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100320 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100321 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100322 }
323
Harald Weltebb7523b2018-03-29 08:52:01 +0200324 type record HostPort {
325 charstring hostname,
326 integer portnr optional
327 }
328 type record RtpFlowData {
329 HostPort em, /* emulation side */
330 HostPort mgw, /* mgw side */
331 uint7_t pt,
332 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200333 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100334 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200335 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
336 MgcpOsmuxCID osmux_cid optional,
337 MgcpOsmuxCID osmux_cid_response optional,
338 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100339 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200340 }
341
Philipp Maier2321ef92018-06-27 17:52:04 +0200342 /* Create an RTP flow (bidirectional, or receive-only) */
343 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 +0200344 boolean one_phase := true)
345 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200346 var template MgcpCommand cmd;
347 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100348 var SDP_attribute_list attributes;
349
350 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
351 if (isvalue(flow.fmtp)) {
352 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
353 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200354
355 /* bind local RTP emulation socket */
356 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
357
Philipp Maier28bb8292018-07-20 17:09:17 +0200358 /* configure rtp-emulation */
359 if (ispresent(flow.rtp_cfg)) {
360 f_rtpem_configure(pt, flow.rtp_cfg);
361 } else {
362 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
363 rtp_cfg.tx_payload_type := flow.pt
364 f_rtpem_configure(pt, rtp_cfg);
365 }
366
Harald Weltebb7523b2018-03-29 08:52:01 +0200367 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200368 /* Connect flow to MGW using a CRCX that also contains an SDP
369 * part that tells the MGW where we are listening for RTP streams
370 * that come from the MGW. We get a fully working connection in
371 * one go. */
372
373 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200374 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100375 flow.em.portnr, { int2str(flow.pt) }, attributes);
376
Harald Weltebb7523b2018-03-29 08:52:01 +0200377 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
378 flow.mgcp_conn_id := extract_conn_id(resp);
379 /* extract port number from response */
380 flow.mgw.portnr :=
381 resp.sdp.media_list[0].media_field.ports.port_number;
382 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200383 /* Create a half-open connection only. We do not tell the MGW
384 * where it can send RTP streams to us. This means this
385 * connection will only be able to receive but can not send
386 * data back to us. In order to turn the connection in a fully
387 * bi-directional one, a separate MDCX is needed. */
388
389 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
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 MGW-side port number from response */
393 flow.mgw.portnr :=
394 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200395 }
396 /* finally, connect the emulation-side RTP socket to the MGW */
397 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
398 }
399
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200400 /* Create an Osmux flow (bidirectional, or receive-only) */
401 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
402 boolean one_phase := true)
403 runs on dummy_CT {
404 var template MgcpCommand cmd;
405 var MgcpResponse resp;
406 var SDP_attribute_list attributes;
407 var OsmuxTxHandle tx_hdl;
408 var OsmuxRxHandle rx_hdl;
409 var charstring cid_response;
410 var OsmuxCID cid_resp_parsed
411
412 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
413 if (isvalue(flow.fmtp)) {
414 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
415 }
416
417 /* bind local Osmux emulation socket */
418 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
419
420 /* configure osmux-emulation */
421 if (ispresent(flow.osmux_cfg)) {
422 f_osmuxem_configure(pt, flow.osmux_cfg);
423 } else {
424 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
425 f_osmuxem_configure(pt, osmux_cfg);
426 flow.osmux_cfg := osmux_cfg
427 }
428
429 if (one_phase) {
430 /* Connect flow to MGW using a CRCX that also contains an SDP
431 * part that tells the MGW where we are listening for Osmux streams
432 * that come from the MGW. We get a fully working connection in
433 * one go. */
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200434 if (flow.osmux_cid != -1) {
435 /* We may still want to negotiate osmux CID later at MDCX */
436 rx_hdl := c_OsmuxemDefaultRxHandle;
437 rx_hdl.cid := flow.osmux_cid;
438 f_osmuxem_register_rxhandle(pt, rx_hdl);
439 flow.osmux_cid_sent := true;
440 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200441 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
442 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
443 flow.em.portnr, { int2str(flow.pt) }, attributes);
444 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
445 flow.mgcp_conn_id := extract_conn_id(resp);
446 /* extract port number from response */
447 flow.mgw.portnr :=
448 resp.sdp.media_list[0].media_field.ports.port_number;
449 } else {
450 /* Create a half-open connection only. We do not tell the MGW
451 * where it can send Osmux streams to us. This means this
452 * connection will only be able to receive but can not send
453 * data back to us. In order to turn the connection in a fully
454 * bi-directional one, a separate MDCX is needed. */
455
456 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
457 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
458
459 flow.mgcp_conn_id := extract_conn_id(resp);
460 /* extract MGW-side port number from response */
461 flow.mgw.portnr :=
462 resp.sdp.media_list[0].media_field.ports.port_number;
463 }
464
465 /* extract Osmux CID we got assigned by the MGW */
466 var MgcpMessage resp_msg := {
467 response := resp
468 }
469
470 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
471 setverdict(fail, "No Osmux CID in MGCP response", resp);
472 mtc.stop;
473 }
474
475 /* Make sure response is no wildcard */
476 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
477 if (flow.osmux_cid_response == -1) {
478 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
479 mtc.stop;
480 }
481 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
482 f_osmuxem_register_txhandle(pt, tx_hdl);
483
484 /* finally, connect the emulation-side RTP socket to the MGW */
485 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
486 }
487
Philipp Maier2321ef92018-06-27 17:52:04 +0200488 /* Modify an existing RTP flow */
489 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
490 runs on dummy_CT {
491 var template MgcpCommand cmd;
492 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100493 var SDP_attribute_list attributes;
494
495 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
496 if (isvalue(flow.fmtp)) {
497 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
498 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200499
500 /* rebind local RTP emulation socket to the new address */
501 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
502
Philipp Maier28bb8292018-07-20 17:09:17 +0200503 /* reconfigure rtp-emulation */
504 if (ispresent(flow.rtp_cfg)) {
505 f_rtpem_configure(pt, flow.rtp_cfg);
506 } else {
507 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
508 rtp_cfg.tx_payload_type := flow.pt
509 f_rtpem_configure(pt, rtp_cfg);
510 }
511
Philipp Maier2321ef92018-06-27 17:52:04 +0200512 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
513 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
514 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100515 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200516 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
517
518 /* extract MGW-side port number from response. (usually this
519 * will not change, but thats is up to the MGW) */
520 flow.mgw.portnr :=
521 resp.sdp.media_list[0].media_field.ports.port_number;
522
523 /* reconnect the emulation-side RTP socket to the MGW */
524 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
525 }
526
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200527 /* Modify an existing Osmux flow */
528 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
529 runs on dummy_CT {
530 var template MgcpCommand cmd;
531 var MgcpResponse resp;
532 var SDP_attribute_list attributes;
533 var OsmuxRxHandle rx_hdl;
534 var charstring cid_response;
535 var OsmuxCID cid_resp_parsed
536
537 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
538 if (isvalue(flow.fmtp)) {
539 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
540 }
541
542 /* rebind local Osmux emulation socket to the new address */
543 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
544
545 /* configure osmux-emulation */
546 if (ispresent(flow.osmux_cfg)) {
547 f_osmuxem_configure(pt, flow.osmux_cfg);
548 } else {
549 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
550 f_osmuxem_configure(pt, osmux_cfg);
551 }
552
553 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
554 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
555 rx_hdl := c_OsmuxemDefaultRxHandle;
556 rx_hdl.cid := flow.osmux_cid;
557 f_osmuxem_register_rxhandle(pt, rx_hdl);
558 flow.osmux_cid_sent := true;
559 }
560
561 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
562 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
563 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
564 flow.em.portnr, { int2str(flow.pt) }, attributes);
565 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
566
567 /* extract MGW-side port number from response. (usually this
568 * will not change, but thats is up to the MGW) */
569 flow.mgw.portnr :=
570 resp.sdp.media_list[0].media_field.ports.port_number;
571
572 /* extract Osmux CID we got assigned by the MGW */
573 var MgcpMessage resp_msg := {
574 response := resp
575 }
576
577 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
578 setverdict(fail, "No Osmux CID in MGCP response", resp);
579 mtc.stop;
580 }
581
582 /* Make sure response is no wildcard */
583 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
584 if (cid_resp_parsed == -1) {
585 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
586 mtc.stop;
587 }
588 if (cid_resp_parsed != flow.osmux_cid_response) {
589 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
590 mtc.stop;
591 }
592
593 /* reconnect the emulation-side Osmux socket to the MGW */
594 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
595 }
596
Philipp Maier2321ef92018-06-27 17:52:04 +0200597 /* Delete an existing RTP flow */
598 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
599 runs on dummy_CT {
600 var template MgcpCommand cmd;
601 var MgcpResponse resp;
602
603 /* Switch off RTP flow */
604 f_rtpem_mode(pt, RTPEM_MODE_NONE);
605
606 /* Delete connection on MGW (if needed) */
607 if (isvalue(call_id) and isvalue(ep)) {
608 f_sleep(0.1);
609 f_dlcx_ok(valueof(ep), call_id);
610 }
611 }
612
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200613 /* Delete an existing Osmux flow */
614 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
615 runs on dummy_CT {
616 var template MgcpCommand cmd;
617 var MgcpResponse resp;
618
619 /* Switch off Osmux flow */
620 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
621
622 /* Delete connection on MGW (if needed) */
623 if (isvalue(call_id) and isvalue(ep)) {
624 f_sleep(0.1);
625 f_dlcx_ok(valueof(ep), call_id);
626 }
627 }
628
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100629 function f_crcx(charstring ep_prefix) runs on dummy_CT {
630 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800631 var template MgcpCommand cmd;
632 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100633 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800634
Harald Welteedc45c12017-11-18 19:15:05 +0100635 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800636
Harald Welteba62c8c2017-11-18 18:26:49 +0100637 /* create the connection on the MGW */
638 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100639 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100640 extract_conn_id(resp);
641
642 /* clean-up */
643 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100644 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100645
Philipp Maier45635f42018-06-05 17:28:02 +0200646 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
647 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
648 var template MgcpCommand cmd;
649 var MgcpResponse resp;
650 var MgcpCallId call_id := '1234'H;
651
652 f_init(ep);
653
654 /* create the connection on the MGW */
655 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
656 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
657 extract_conn_id(resp);
658
659 /* clean-up */
660 f_dlcx_ok(ep, call_id);
661
662 /* See also OS#2658: Even when we omit the LCO information, we
663 expect the MGW to pick a sane payload type for us. This
664 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200665 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200666 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200667 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200668 }
669
670 /* See also OS#2658: We also expect the MGW to assign a port
671 number to us. */
672 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
673 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200674 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200675 }
676 }
677
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200678 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
679 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
680 var template MgcpCommand cmd;
681 var MgcpResponse resp;
682 var MgcpCallId call_id := '1234'H;
683 var charstring cid_response;
684
685 if (run_init) {
686 f_init(ep, true);
687 }
688
689 /* create the connection on the MGW */
690 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
691 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
692 extract_conn_id(resp);
693
694 /* extract Osmux CID we got assigned by the MGW */
695 var MgcpMessage resp_msg := {
696 response := resp
697 }
698
699 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
700 setverdict(fail, "No Osmux CID in MGCP response", resp);
701 mtc.stop;
702 }
703
704 /* Make sure response is no wildcard */
705 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
706 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
707 mtc.stop;
708 }
709
710 /* clean-up */
711 f_dlcx_ok(ep, call_id);
712 }
713
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100714 /* test valid CRCX without SDP */
715 testcase TC_crcx() runs on dummy_CT {
716 f_crcx(c_mgw_ep_rtpbridge);
717 setverdict(pass);
718 }
719
Philipp Maier45635f42018-06-05 17:28:02 +0200720 /* test valid CRCX without SDP and LCO */
721 testcase TC_crcx_no_lco() runs on dummy_CT {
722 f_crcx_no_lco(c_mgw_ep_rtpbridge);
723 setverdict(pass);
724 }
725
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100726 /* test valid CRCX without SDP (older method without endpoint prefix) */
727 testcase TC_crcx_noprefix() runs on dummy_CT {
728 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800729 setverdict(pass);
730 }
731
732 /* test CRCX with unsupported mode, expect 517 */
733 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
734 var template MgcpCommand cmd;
735 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100736 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100737 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800738 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
739
Harald Welteedc45c12017-11-18 19:15:05 +0100740 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800741
Harald Welteba62c8c2017-11-18 18:26:49 +0100742 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800743 resp := mgcp_transceive_mgw(cmd, rtmpl);
744 setverdict(pass);
745 }
746
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200747 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
748 testcase TC_crcx_osmo_ign() runs on dummy_CT {
749 var template MgcpCommand cmd;
750 var MgcpResponse resp;
751 var MgcpEndpoint ep := "7@" & c_mgw_domain;
752 var MgcpCallId call_id := '3'H;
753
754 f_init(ep);
755
756 /* CRCX 1 7@mgw MGCP 1.0
757 C: 3
758 L: p:20, a:GSM-EFR, nt:IN
759 M: recvonly
760 X-Osmo-IGN: C
761 */
762
763 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
764 cmd.params := {ts_MgcpParCallId(call_id),
765 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
766 t_MgcpParConnMode("recvonly"),
767 t_MgcpParOsmoIGN("C")};
768 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
769 extract_conn_id(resp);
770
771 /* clean-up */
772 f_dlcx_ok(ep, call_id);
773 setverdict(pass);
774 }
775
Harald Welte21ba5572017-09-19 17:55:05 +0800776 /* test CRCX with early bi-directional mode, expect 527 as
777 * bi-diretional media can only be established once both local and
778 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800779 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
780 var template MgcpCommand cmd;
781 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100782 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100783 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800784 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
785
Harald Welteedc45c12017-11-18 19:15:05 +0100786 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800787
Harald Welteba62c8c2017-11-18 18:26:49 +0100788 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800789 resp := mgcp_transceive_mgw(cmd, rtmpl);
790 setverdict(pass);
791 }
792
793 /* test CRCX with unsupported Parameters */
794 testcase TC_crcx_unsupp_param() runs on dummy_CT {
795 var template MgcpCommand cmd;
796 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100797 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100798 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800799 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
800
Harald Welteedc45c12017-11-18 19:15:05 +0100801 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800802
Harald Welteba62c8c2017-11-18 18:26:49 +0100803 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100804 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
805 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
806
Harald Weltee636afd2017-09-17 16:24:09 +0800807 resp := mgcp_transceive_mgw(cmd, rtmpl);
808 setverdict(pass);
809 }
810
811 /* test CRCX with missing CallId */
812 testcase TC_crcx_missing_callid() runs on dummy_CT {
813 var template MgcpCommand cmd;
814 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100815 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100816 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800817
Harald Welteedc45c12017-11-18 19:15:05 +0100818 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800819
Harald Welteba62c8c2017-11-18 18:26:49 +0100820 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800821 cmd.params := {
822 t_MgcpParConnMode("recvonly"),
823 t_MgcpParLocConnOpt("p:20")
824 }
825 resp := mgcp_transceive_mgw(cmd, rtmpl);
826 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100827
Harald Weltee636afd2017-09-17 16:24:09 +0800828 }
829
830 /* test CRCX with missing Mode */
831 testcase TC_crcx_missing_mode() runs on dummy_CT {
832 var template MgcpCommand cmd;
833 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100834 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100835 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100836 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800837
Harald Welteedc45c12017-11-18 19:15:05 +0100838 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800839
Harald Welteba62c8c2017-11-18 18:26:49 +0100840 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800841 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100842 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800843 t_MgcpParLocConnOpt("p:20")
844 }
845 resp := mgcp_transceive_mgw(cmd, rtmpl);
846 setverdict(pass);
847 }
848
849 /* test CRCX with unsupported packetization interval */
850 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
851 var template MgcpCommand cmd;
852 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100853 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100854 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100855 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800856
Harald Welteedc45c12017-11-18 19:15:05 +0100857 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800858
Harald Welteba62c8c2017-11-18 18:26:49 +0100859 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100860 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800861 resp := mgcp_transceive_mgw(cmd, rtmpl);
862 setverdict(pass);
863 }
864
865 /* test CRCX with illegal double presence of local connection option */
866 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
867 var template MgcpCommand cmd;
868 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100869 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100870 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800871 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
872
Harald Welteedc45c12017-11-18 19:15:05 +0100873 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800874
Harald Welteba62c8c2017-11-18 18:26:49 +0100875 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100876 /* p:20 is permitted only once and not twice! */
877 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800878 resp := mgcp_transceive_mgw(cmd, rtmpl);
879 setverdict(pass);
880 }
881
882 /* test valid CRCX with valid SDP */
883 testcase TC_crcx_sdp() runs on dummy_CT {
884 var template MgcpCommand cmd;
885 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100886 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100887 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800888
Harald Welteedc45c12017-11-18 19:15:05 +0100889 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800890
Harald Welteba62c8c2017-11-18 18:26:49 +0100891 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800892 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
893 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
894 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100895 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100896
897 /* clean-up */
898 f_dlcx_ok(ep, call_id);
899
Harald Weltee636afd2017-09-17 16:24:09 +0800900 setverdict(pass);
901 }
902
Philipp Maier5e06cee2018-02-01 18:28:08 +0100903 /* test valid wildcarded CRCX */
904 testcase TC_crcx_wildcarded() runs on dummy_CT {
905 var template MgcpCommand cmd;
906 var MgcpResponse resp;
907 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
908 var MgcpCallId call_id := '1234'H;
909 var MgcpEndpoint ep_assigned;
910 f_init();
911
912 /* create the connection on the MGW */
913 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
914 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
915 extract_conn_id(resp);
916
917 /* extract endpoint name we got assigned by the MGW */
918 var MgcpMessage resp_msg := {
919 response := resp
920 }
921 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
922 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200923 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100924 }
925
926 /* clean-up */
927 f_dlcx_ok(ep_assigned, call_id);
928
929 setverdict(pass);
930 }
931
932 /* test valid wildcarded CRCX */
933 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maierc60e8472020-07-08 12:57:13 +0200934 const integer n_endpoints := 31;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100935 var integer i;
936 var template MgcpCommand cmd;
937 var MgcpResponse resp;
938 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
939 var MgcpCallId call_id := '1234'H;
940 var MgcpEndpoint ep_assigned[n_endpoints];
941 f_init();
942
943 /* Exhaust all endpoint resources on the virtual trunk */
944 for (i := 0; i < n_endpoints; i := i+1) {
945 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
946 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
947
948 /* Make sure we got a connection id */
949 extract_conn_id(resp);
950
951 var MgcpMessage resp_msg := {
952 response := resp
953 }
954 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
955 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200956 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100957 }
958 }
959
960 /* Try to allocate one more endpoint, which should fail */
961 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
962 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
963 resp := mgcp_transceive_mgw(cmd, rtmpl);
964 setverdict(pass);
965
966 /* clean-up */
967 for (i := 0; i < n_endpoints; i := i+1) {
968 f_dlcx_ok(ep_assigned[i], call_id);
969 }
970 setverdict(pass);
971 }
972
Harald Weltee636afd2017-09-17 16:24:09 +0800973 /* TODO: various SDP related bits */
974
975
976 /* TODO: CRCX with X-Osmux */
977 /* TODO: double CRCX without force_realloc */
978
979 /* TODO: MDCX (various) */
980
981 /* TODO: MDCX without CRCX first */
982 testcase TC_mdcx_without_crcx() runs on dummy_CT {
983 var template MgcpCommand cmd;
984 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100985 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100986 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800987 var template MgcpResponse rtmpl := {
988 line := {
989 /* TODO: accept/enforce better error? */
990 code := "400",
991 string := ?
992 },
993 params:= { },
994 sdp := omit
995 };
996
Harald Welteedc45c12017-11-18 19:15:05 +0100997 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800998
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100999 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001000 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1001 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1002 valueof(ts_SDP_ptime(20)) });
1003 resp := mgcp_transceive_mgw(cmd, rtmpl);
1004 setverdict(pass);
1005 }
1006
1007 /* DLCX without CRCX first */
1008 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1009 var template MgcpCommand cmd;
1010 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001011 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001012 var template MgcpResponse rtmpl := {
1013 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001014 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001015 string := ?
1016 },
1017 params:= { },
1018 sdp := omit
1019 };
1020
Harald Welteedc45c12017-11-18 19:15:05 +01001021 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001022
Harald Welteedc45c12017-11-18 19:15:05 +01001023 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001024 resp := mgcp_transceive_mgw(cmd, rtmpl);
1025 setverdict(pass);
1026 }
1027
Philipp Maier21c1cff2021-07-20 14:22:53 +02001028 /* DLCX to non existing endpoint */
1029 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1030 var template MgcpCommand cmd;
1031 var MgcpResponse resp;
1032 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "AA@" & c_mgw_domain;
1033 var template MgcpResponse rtmpl := {
1034 line := {
1035 code := ("500"),
1036 string := ?
1037 },
1038 params:= { },
1039 sdp := omit
1040 };
1041
1042 f_init(ep);
1043
1044 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1045 resp := mgcp_transceive_mgw(cmd, rtmpl);
1046 setverdict(pass);
1047 }
1048
Philipp Maier8a3dc922018-02-02 14:55:12 +01001049 /* test valid wildcarded MDCX */
1050 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1051 /* Note: A wildcarded MDCX is not allowed, so we expect the
1052 * MGW to reject this request */
1053 var template MgcpCommand cmd;
1054 var MgcpResponse resp;
1055 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1056 var MgcpCallId call_id := '1225'H;
1057 var template MgcpResponse rtmpl := {
1058 line := {
1059 /* TODO: accept/enforce better error? */
1060 code := "507",
1061 string := ?
1062 },
1063 params:= { },
1064 sdp := omit
1065 };
1066
1067 f_init(ep);
1068
1069 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1070 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1071 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1072 valueof(ts_SDP_ptime(20)) });
1073 resp := mgcp_transceive_mgw(cmd, rtmpl);
1074 setverdict(pass);
1075 }
1076
1077 /* test valid wildcarded DLCX */
1078 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001079 var template MgcpCommand cmd;
1080 var MgcpResponse resp;
1081 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001082 const integer n_endpoints := 31;
1083 var integer i;
1084 var MgcpCallId call_id := '1234'H;
1085 var StatsDExpects expect;
1086 f_init(ep);
1087
1088 /* Allocate a few endpoints */
1089 for (i := 0; i < n_endpoints; i := i+1) {
1090 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1091 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1092 }
1093
1094 expect := {
1095 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := n_endpoints, max := n_endpoints}
1096 };
1097 f_statsd_expect(expect);
1098
1099 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001100 var template MgcpResponse rtmpl := {
1101 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001102 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001103 string := ?
1104 },
1105 params:= { },
1106 sdp := omit
1107 };
Philipp Maier55b90542021-07-02 12:33:19 +02001108 cmd := ts_DLCX(get_next_trans_id(), ep);
1109 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001110
Philipp Maier55b90542021-07-02 12:33:19 +02001111 /* The stats reporter collects multiple samples during the reporting interval and
1112 * reports the highest back to the user. This means we will not immediately get
1113 * the 0 endpoints but an intermediate result instead. */
1114 expect := {
1115 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := n_endpoints}
1116 };
1117 f_statsd_expect(expect);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001118
Philipp Maier55b90542021-07-02 12:33:19 +02001119 /* The second interval must resturn a result with 0 endpoints in use. */
1120 expect := {
1121 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1122 };
1123 f_statsd_expect(expect);
1124
Philipp Maier8a3dc922018-02-02 14:55:12 +01001125 setverdict(pass);
1126 }
1127
Harald Welte79181ff2017-11-18 19:26:11 +01001128 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1129 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1130 var template MgcpCommand cmd;
1131 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001132 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001133 var MgcpCallId call_id := '51234'H;
1134
1135 f_init(ep);
1136
1137 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1138 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1139
1140 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1141
1142 setverdict(pass);
1143 }
1144
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001145 /* test valid CRCX without SDP */
1146 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1147 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1148 setverdict(pass);
1149 }
1150
1151 /* test valid CRCX without SDP */
1152 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1153 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1154 setverdict(pass);
1155 }
1156
1157 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1158 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1159 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1160 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1161 setverdict(pass);
1162 }
1163
1164 /* Create one half open connection in receive-only mode. The MGW must accept
1165 * the packets but must not send any. */
1166 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1167 var RtpFlowData flow;
1168 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1169 var MgcpCallId call_id := '1225'H;
1170 var OsmuxemStats stats;
1171 var OsmuxTxHandle tx_hdl;
1172
1173 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001174 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001175 flow.em.portnr := mp_local_osmux_port;
1176 flow.osmux_cid := -1;
1177 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1178
1179 /* create a transmitter not yet known by MGW */
1180 tx_hdl := valueof(t_TxHandleAMR590(2));
1181 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1182
1183 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1184 f_sleep(1.0);
1185 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1186
1187 stats := f_osmuxem_stats_get(OsmuxEM);
1188
1189 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1190 setverdict(fail);
1191 }
1192 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1193 setverdict(fail);
1194 }
1195
1196 f_osmuxem_stats_err_check(stats);
1197
1198 setverdict(pass);
1199 }
1200
1201 /* Create one connection in loopback mode, test if the Osmux packets are
1202 * actually reflected */
1203 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1204 var RtpFlowData flow;
1205 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1206 var MgcpCallId call_id := '1225'H;
1207 var OsmuxemStats stats;
1208 var OsmuxTxHandle tx_hdl;
1209
1210 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001211 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001212 flow.em.portnr := mp_local_osmux_port;
1213 flow.osmux_cid := 2;
1214 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1215
1216 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1217 f_sleep(1.0);
1218
1219 /* Switch off both Tx, wait to receive delayed frames from MGW */
1220 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1221 f_sleep(0.1);
1222 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1223
1224 stats := f_osmuxem_stats_get(OsmuxEM);
1225
1226 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1227 setverdict(fail);
1228 }
1229 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1230 setverdict(fail);
1231 }
1232
1233 f_osmuxem_stats_err_check(stats);
1234
1235 setverdict(pass);
1236 }
1237
1238 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1239 * must match the reception statistics on the other side and vice versa. The
1240 * user may also supply a tolerance value (number of packets) when deviations
1241 * are acceptable */
1242 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1243 var integer plen;
1244
1245 log("stats A: ", a);
1246 log("stats B: ", b);
1247 log("tolerance: ", tolerance, " packets");
1248 log("batch_size: ", batch_size, " packets");
1249
1250 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1251
1252 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1253 return false;
1254 }
1255
1256 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1257 return false;
1258 }
1259
1260 if(a.num_pkts_tx > 0) {
1261 plen := a.bytes_payload_tx / a.num_pkts_tx;
1262 } else {
1263 plen := 0;
1264 }
1265
1266 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1267 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1268 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1269 return false;
1270 }
1271
1272 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) {
1273 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1274 return false;
1275 }
1276
1277 return true;
1278 }
1279
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001280 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1281 charstring local_ip_rtp, charstring remote_ip_rtp,
1282 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001283 var RtpFlowData flow[2];
1284 var RtpemStats stats_rtp;
1285 var OsmuxemStats stats_osmux;
1286 var MgcpResponse resp;
1287 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1288 var MgcpCallId call_id := '1226'H;
1289 var integer tolerance := 0;
1290
1291 f_init(ep, true);
1292
1293 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001294 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001295 flow[0].rtp_cfg := c_RtpemDefaultCfg
1296 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1297 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1298 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);
1299 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1300 /* bind local RTP emulation sockets */
1301 flow[0].em.portnr := 10000;
1302 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1303
1304 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001305 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001306 flow[1].em.portnr := mp_local_osmux_port;
1307 flow[1].osmux_cid := 2;
1308 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1309 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1310
1311 if (bidir) {
1312 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1313 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1314
1315 /* Note: When we test bidirectional we may
1316 * loose packets during switch off because
1317 * both ends are transmitting and we only
1318 * can switch them off one by one. */
1319 tolerance := 3;
1320 } else {
1321 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1322 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1323 }
1324
1325 f_sleep(1.0);
1326
1327 /* Switch off both Tx, wait to receive delayed frames from MGW */
1328 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1329 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1330 f_sleep(0.1);
1331
1332 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1333 f_flow_delete(RTPEM[1]);
1334
1335 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1336 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1337 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1338 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1339 mtc.stop;
1340 }
1341
1342 f_rtpem_stats_err_check(stats_rtp);
1343 f_osmuxem_stats_err_check(stats_osmux);
1344
1345 setverdict(pass);
1346 }
1347
1348 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1349 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001350 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1351 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001352 }
1353
1354 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1355 * exchange some data in both directions */
1356 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001357 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1358 mp_local_ipv4, mp_remote_ipv4);
1359 }
1360
1361 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1362 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1363 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1364 mp_local_ipv6, mp_remote_ipv6);
1365 }
1366 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1367 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1368 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1369 mp_local_ipv6, mp_remote_ipv6);
1370 }
1371 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1372 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1373 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1374 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001375 }
1376
1377
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001378 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1379 charstring local_ip_rtp, charstring remote_ip_rtp,
1380 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001381 var RtpFlowData flow[2];
1382 var RtpemStats stats_rtp;
1383 var OsmuxemStats stats_osmux;
1384 var MgcpResponse resp;
1385 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1386 var MgcpCallId call_id := '1227'H;
1387 var integer num_pkts_tx[2];
1388 var integer temp;
1389
1390 f_init(ep, true);
1391
1392 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001393 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001394 flow[0].rtp_cfg := c_RtpemDefaultCfg
1395 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1396 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1397 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);
1398 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1399 /* bind local RTP emulation sockets */
1400 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001401 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001402
1403
1404 /* Create the second connection. This connection will be also
1405 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001406 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001407 flow[1].em.portnr := mp_local_osmux_port;
1408 if (crcx_osmux_wildcard) {
1409 flow[1].osmux_cid := -1;
1410 } else {
1411 flow[1].osmux_cid := 2;
1412 }
1413 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001414 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001415
1416
1417 /* The first leg starts transmitting */
1418 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1419 f_sleep(0.5);
1420 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1421 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1422 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1423 mtc.stop;
1424 }
1425 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1426 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1427 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1428 mtc.stop;
1429 }
1430
1431 /* The second leg starts transmitting a little later */
1432 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1433 f_sleep(1.0);
1434 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1435 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1436 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1437 mtc.stop;
1438 }
1439 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1440 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1441 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1442 mtc.stop;
1443 }
1444
1445 /* The first leg will now be switched into bidirectional
1446 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001447 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1448 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1449 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001450 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1451 f_sleep(0.5);
1452 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1453 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1454 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1455 mtc.stop;
1456 }
1457 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1458 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1459 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1460 mtc.stop;
1461 }
1462
1463 /* When the second leg is switched into bidirectional mode
1464 * as well, then the MGW will connect the two together and
1465 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001466 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1467 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001468 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001469
1470 if (crcx_osmux_wildcard) {
1471 /* For now we must set same CID as the MGW recvCID,
1472 * having sendCID!=recvCID is not yet supported. */
1473 flow[1].osmux_cid := flow[1].osmux_cid_response;
1474 }
1475 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1476 f_sleep(2.0);
1477
1478 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1479 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1480
1481 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1482 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1483 log("stats_rtp: ", stats_rtp);
1484 log("stats_osmux: ", stats_osmux);
1485 log("old_rtp_tx: ", num_pkts_tx[0]);
1486 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1487 mtc.stop;
1488 }
1489
1490 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1491 if (temp > 3 or temp < -3) {
1492 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1493 mtc.stop;
1494 }
1495
1496 f_rtpem_stats_err_check(stats_rtp);
1497 f_osmuxem_stats_err_check(stats_osmux);
1498
1499 /* Tear down */
1500 f_flow_delete(RTPEM[0]);
1501 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1502 setverdict(pass);
1503 }
1504
1505 /* create one RTP and one OSmux emulations and pass data in both
1506 directions. Create CRCX with wildcard Osmux CID and set it later
1507 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1508 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001509 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1510 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001511 }
1512
1513 /* create one RTP and one OSmux emulations and pass data in both
1514 directions. Create CRCX with fixed Osmux CID and keep it during
1515 MDCX. This is similar to how BSC sets up the call in AoIP. */
1516 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001517 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1518 mp_local_ipv4, mp_remote_ipv4);
1519 }
1520
1521 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1522 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1523 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1524 mp_local_ipv6, mp_remote_ipv6);
1525 }
1526 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1527 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1528 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1529 mp_local_ipv6, mp_remote_ipv6);
1530 }
1531 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1532 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1533 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1534 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001535 }
1536
Harald Welte646ecdb2017-12-28 03:21:57 +01001537 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1538 var template MgcpCommand cmd;
1539 var MgcpResponse resp;
1540
1541 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1542 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1543
1544 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1545
1546 setverdict(pass);
1547 }
1548
1549 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1550 var MgcpEndpoint ep;
1551 var MgcpCallId call_id;
1552 var integer ep_nr;
1553
1554 f_init();
1555
1556 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001557 if(ep_nr > 15) {
1558 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1559 } else {
1560 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1561 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001562 call_id := int2hex(ep_nr, 2) & '1234'H;
1563 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1564 }
1565 }
1566
Harald Welte79181ff2017-11-18 19:26:11 +01001567 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1568 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001569 var template MgcpCommand cmd;
1570 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001571 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001572 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001573
Harald Welteedc45c12017-11-18 19:15:05 +01001574 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001575
Harald Welteba62c8c2017-11-18 18:26:49 +01001576 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001577 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001578
Harald Welteba62c8c2017-11-18 18:26:49 +01001579 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001580
1581 setverdict(pass);
1582 }
1583
Harald Welte79181ff2017-11-18 19:26:11 +01001584 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1585 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1586 var template MgcpCommand cmd;
1587 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001588 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001589 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001590
1591 f_init(ep);
1592
1593 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1594 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1595
1596 f_dlcx_ok(ep);
1597
1598 setverdict(pass);
1599 }
1600
1601
Harald Welte6d167f82017-11-18 19:41:35 +01001602 /* CRCX + DLCX of valid endpoint but invalid call-id */
1603 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1604 var template MgcpCommand cmd;
1605 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001606 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001607 var MgcpCallId call_id := '51231'H;
1608
1609 f_init(ep);
1610
1611 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1612 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1613
1614 f_dlcx(ep, "516", *, 'ffff'H);
1615
1616 setverdict(pass);
1617 }
1618
1619
1620 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1621 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1622 var template MgcpCommand cmd;
1623 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001624 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001625 var MgcpCallId call_id := '51230'H;
1626
1627 f_init(ep);
1628
1629 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1630 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1631
1632 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1633
1634 setverdict(pass);
1635 }
1636
1637
Harald Weltee636afd2017-09-17 16:24:09 +08001638 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001639 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1640 var template MgcpCommand cmd;
1641 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001642 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001643 var MgcpCallId call_id := '51229'H;
1644 var template MgcpResponse rtmpl := {
1645 line := {
1646 code := "200",
1647 string := "OK"
1648 },
1649 params:= { },
1650 sdp := omit
1651 };
1652
1653 f_init(ep);
1654
1655 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1656 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1657
1658 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1659 resp := mgcp_transceive_mgw(cmd, rtmpl);
1660 resp := mgcp_transceive_mgw(cmd, rtmpl);
1661
1662 setverdict(pass);
1663 }
1664
Harald Weltebb7523b2018-03-29 08:52:01 +02001665 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1666 charstring codec) := {
1667 em := {
1668 hostname := host_a,
1669 portnr := omit
1670 },
1671 mgw := {
1672 hostname := host_b,
1673 portnr := omit
1674 },
1675 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001676 codec := codec,
1677 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001678 }
Harald Weltef53f1642017-11-18 19:57:11 +01001679
Harald Weltebb7523b2018-03-29 08:52:01 +02001680 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1681 testcase TC_rtpem_selftest() runs on dummy_CT {
1682 var RtpemStats stats[2];
1683 var integer local_port := 10000;
1684 var integer local_port2 := 20000;
1685
1686 f_init();
1687
1688 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1689 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1690
1691 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1692 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1693
1694 log("=== starting");
1695 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1696 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1697
1698 f_sleep(5.0);
1699
1700 log("=== stopping");
1701 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1702 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1703 f_sleep(0.5);
1704 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1705 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1706
1707 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1708 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1709 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1710 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001711 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001712 }
1713 setverdict(pass);
1714 }
1715
Philipp Maier2321ef92018-06-27 17:52:04 +02001716 /* Create one half open connection in receive-only mode. The MGW must accept
1717 * the packets but must not send any. */
1718 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1719 var RtpFlowData flow;
1720 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1721 var MgcpCallId call_id := '1225'H;
1722 var RtpemStats stats;
1723
1724 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001725 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001726 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001727 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001728
1729 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1730 f_sleep(1.0);
1731 f_flow_delete(RTPEM[0], ep, call_id);
1732
1733 stats := f_rtpem_stats_get(RTPEM[0]);
1734
Philipp Maier42b17cc2019-10-01 13:53:17 +02001735 /* Make sure that at least some amount of RTP packets/bytes
1736 * have has been transmitted. The compare values for
1737 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1738 * using a testrun and the results were devided by 2, so even
1739 * in load situations we should reach the minimum amount of
1740 * required packets/bytes */
1741
1742 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001743 setverdict(fail);
1744 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001745 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001746 setverdict(fail);
1747 }
Philipp Maier36291392018-07-25 09:40:44 +02001748
1749 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001750
1751 setverdict(pass);
1752 }
1753
1754 /* Create one connection in loopback mode, test if the RTP packets are
1755 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001756 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 +02001757 var RtpFlowData flow;
1758 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1759 var MgcpCallId call_id := '1225'H;
1760 var RtpemStats stats;
1761
1762 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001763 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001764 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001765 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001766
1767 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1768 f_sleep(1.0);
1769 f_flow_delete(RTPEM[0], ep, call_id);
1770
1771 stats := f_rtpem_stats_get(RTPEM[0]);
1772
1773 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1774 setverdict(fail);
1775 }
1776 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1777 setverdict(fail);
1778 }
Philipp Maier36291392018-07-25 09:40:44 +02001779
1780 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001781
1782 setverdict(pass);
1783 }
1784
Philipp Maier1ac13982021-05-07 23:06:07 +02001785 /* Create one connection in loopback mode, test if the RTP packets are
1786 * actually reflected */
1787 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001788 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001789 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001790 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1791 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1792 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001793
1794 /* Same as above, but we will intenionally not tell the MGW where to
1795 * send the outgoing traffic. The connection is still created in
1796 * loopback mode, so the MGW should take the originating address from
1797 * the incoming RTP packet and send it back to the source */
1798 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001799 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001800 }
1801
1802
Philipp Maier7df85f62018-07-25 10:26:09 +02001803 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1804 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001805 var RtpFlowData flow[2];
1806 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001807 var MgcpResponse resp;
1808 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1809 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001810 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001811
1812 f_init(ep);
1813
1814 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001815 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001816 /* bind local RTP emulation sockets */
1817 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001818 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001819
1820 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001821 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001822 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001823 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1824
1825 if (bidir) {
1826 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1827 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1828
1829 /* Note: When we test bidirectional we may
1830 * loose packets during switch off because
1831 * both ends are transmitting and we only
1832 * can switch them off one by one. */
1833 tolerance := 3;
1834 } else {
1835 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1836 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1837 }
1838
1839 f_sleep(1.0);
1840
1841 f_flow_delete(RTPEM[1]);
1842 f_flow_delete(RTPEM[0], ep, call_id);
1843
1844 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1845 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1846 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1847 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001848 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001849 }
1850
Philipp Maier36291392018-07-25 09:40:44 +02001851 f_rtpem_stats_err_check(stats[0]);
1852 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001853
Philipp Maier2321ef92018-06-27 17:52:04 +02001854 setverdict(pass);
1855 }
1856
1857 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1858 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001859 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001860 }
1861
1862 /* create two local RTP emulations; create two connections on MGW EP,
1863 * exchange some data in both directions */
1864 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001865 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1866 }
1867
1868 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1869 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1870 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1871 }
1872
1873 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1874 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1875 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001876 }
1877
1878 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001879 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1880 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001881 var RtpFlowData flow[2];
1882 var RtpemStats stats[2];
1883 var MgcpResponse resp;
1884 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1885 var MgcpCallId call_id := '1227'H;
1886 var integer num_pkts_tx[2];
1887 var integer temp;
1888
1889 f_init(ep);
1890
1891 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001892 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001893 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001894 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001895
1896 /* Create the second connection. This connection will be also
1897 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001898 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001899 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001900 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001901
1902 /* The first leg starts transmitting */
1903 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1904 f_sleep(0.5);
1905 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1906 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001907 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001908 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001909 }
1910 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1911 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001912 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001913 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001914 }
1915
1916 /* The second leg starts transmitting a little later */
1917 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1918 f_sleep(1.0);
1919 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1920 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001921 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001922 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001923 }
1924 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1925 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001926 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001927 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001928 }
1929
1930 /* The first leg will now be switched into bidirectional
1931 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001932 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1933 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1934 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001935 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1936 f_sleep(0.5);
1937 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001938 if (stats[0].num_pkts_rx_err_disabled != 0) {
1939 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001940 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001941 }
1942 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1943 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001944 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001945 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001946 }
1947
1948 /* When the second leg is switched into bidirectional mode
1949 * as well, then the MGW will connect the two together and
1950 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02001951 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1952 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001953 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001954 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1955 f_sleep(2.0);
1956
1957 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1958 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1959
1960 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1961 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001962 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001963 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001964 }
1965
1966 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1967 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001968 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001969 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001970 }
Philipp Maier36291392018-07-25 09:40:44 +02001971
1972 f_rtpem_stats_err_check(stats[0]);
1973 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001974
1975 /* Tear down */
1976 f_flow_delete(RTPEM[0]);
1977 f_flow_delete(RTPEM[1], ep, call_id);
1978 setverdict(pass);
1979 }
1980
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001981 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1982 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1983 mp_local_ipv4, mp_remote_ipv4);
1984 }
1985
1986 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
1987 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
1988 mp_local_ipv6, mp_remote_ipv6);
1989 }
1990
1991 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
1992 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1993 mp_local_ipv6, mp_remote_ipv6);
1994 }
1995
Philipp Maier2321ef92018-06-27 17:52:04 +02001996 /* Test what happens when two RTP streams from different sources target
1997 * a single connection. Is the unsolicited stream properly ignored? */
1998 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1999 var RtpFlowData flow[2];
2000 var RtpemStats stats[2];
2001 var MgcpResponse resp;
2002 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2003 var MgcpCallId call_id := '1234321326'H;
2004 var integer unsolicited_port := 10002;
2005
2006 f_init(ep);
2007
2008 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002009 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002010 /* bind local RTP emulation sockets */
2011 flow[0].em.portnr := 10000;
2012 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2013
2014 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002015 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002016 flow[1].em.portnr := 20000;
2017 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002018
2019 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2020 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2021
Philipp Maier2321ef92018-06-27 17:52:04 +02002022 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002023
Philipp Maier2321ef92018-06-27 17:52:04 +02002024 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002025 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2026 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002027 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2028
2029 f_sleep(0.5);
2030
Daniel Willmanna069d382018-12-13 13:53:33 +01002031 /* Stop transmitting packets and tear down the flows */
2032 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002033 f_flow_delete(RTPEM[0]);
2034 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002035
2036 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2037 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2038 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2039 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002040 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002041 }
2042
Philipp Maier36291392018-07-25 09:40:44 +02002043 f_rtpem_stats_err_check(stats[0]);
2044 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002045
Harald Weltebb7523b2018-03-29 08:52:01 +02002046 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002047 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002048
Philipp Maier2321ef92018-06-27 17:52:04 +02002049 /* Test a handover situation. We first create two connections transmit
2050 * some data bidirectionally. Then we will simulate a handover situation. */
2051 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2052 var RtpFlowData flow[2];
2053 var RtpemStats stats[3];
2054 var MgcpResponse resp;
2055 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2056 var MgcpCallId call_id := '76338'H;
2057 var integer port_old;
2058
2059 f_init(ep);
2060
2061 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002062 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002063 /* bind local RTP emulation sockets */
2064 flow[0].em.portnr := 10000;
2065 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2066
2067 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002068 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002069 flow[1].em.portnr := 20000;
2070 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2071
2072 /* Normal rtp flow for one second */
2073 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2074 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2075 f_sleep(1.0);
2076
2077 /* Now switch the flow over to a new port (BTS) */
2078 port_old := flow[0].em.portnr;
2079 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002080 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002081 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002082 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002083
2084 /* When handing over a call, the old source may still keep
2085 * transmitting for a while. We simulate this by injecting
2086 * some unsolicited packets on the behalf of the old source,
2087 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002088 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2089 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002090 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2091 f_sleep(1.0);
2092 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2093 f_sleep(1.0);
2094
2095 /* Terminate call */
2096 f_flow_delete(RTPEM[0]);
2097 f_flow_delete(RTPEM[1], ep, call_id);
2098
2099 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2100 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2101 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2102 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002103 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002104 }
2105 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2106 if (stats[2].num_pkts_rx_err_disabled != 0) {
2107 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002108 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002109 }
2110
Philipp Maier36291392018-07-25 09:40:44 +02002111 f_rtpem_stats_err_check(stats[0]);
2112 f_rtpem_stats_err_check(stats[1]);
2113 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002114
Philipp Maier2321ef92018-06-27 17:52:04 +02002115 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002116 }
Harald Weltef53f1642017-11-18 19:57:11 +01002117
Philipp Maier6d4e0942019-02-21 17:35:01 +01002118
2119 /* create two local RTP emulations; create two connections on MGW EP, see if
2120 * exchanged data is converted bwtween ts101318 and rfc5993 */
2121 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2122 var RtpFlowData flow[2];
2123 var RtpemStats stats[2];
2124 var MgcpResponse resp;
2125 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2126 var MgcpCallId call_id := '1226'H;
2127
2128 f_init(ep);
2129
2130 /* Turn on conversion mode */
2131 f_vty_enter_config(MGWVTY);
2132 f_vty_transceive(MGWVTY, "mgcp");
2133 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2134
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002135 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002136 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002137 /* bind local RTP emulation sockets */
2138 flow[0].em.portnr := 10000;
2139 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2140 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2141 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2142 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2143 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2144
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002145 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002146 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002147 flow[1].em.portnr := 20000;
2148 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2149 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2150 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2151 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2152 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2153
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002154 /* Send RTP packets to connection #0, receive on connection #1 */
2155 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2156 f_sleep(0.5);
2157 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002158 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002159 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2160 f_sleep(0.5);
2161 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002162
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002163 /* Send RTP packets to connection #1, receive on connection #0 */
2164 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2165 f_sleep(0.5);
2166 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2167 f_sleep(1.0);
2168 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2169 f_sleep(0.5);
2170 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2171
2172 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002173 f_flow_delete(RTPEM[0]);
2174 f_flow_delete(RTPEM[1], ep, call_id);
2175
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002176 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002177 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2178 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002179 f_rtpem_stats_err_check(stats[0]);
2180 f_rtpem_stats_err_check(stats[1]);
2181
2182 /* Turn off conversion mode */
2183 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2184
2185 setverdict(pass);
2186 }
2187
Philipp Maier4f764ce2019-03-07 10:54:10 +01002188 /* create two local RTP emulations; create two connections on MGW EP, see if
2189 * exchanged data is converted between AMR octet-aligned and bandwith
2190 * efficient-mode */
2191 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2192 var RtpFlowData flow[2];
2193 var RtpemStats stats[2];
2194 var MgcpResponse resp;
2195 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2196 var MgcpCallId call_id := '1226'H;
2197
2198 f_init(ep);
2199
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002200 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002201 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002202 /* bind local RTP emulation sockets */
2203 flow[0].em.portnr := 10000;
2204 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2205 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2206 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2207 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2208 flow[0].fmtp := fmtp0;
2209 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2210
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002211 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002212 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002213 flow[1].em.portnr := 20000;
2214 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2215 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2216 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2217 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2218 flow[1].fmtp := fmtp1;
2219 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2220
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002221 /* Send RTP packets to connection #0, receive on connection #1 */
2222 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2223 f_sleep(0.5);
2224 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002225 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002226 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2227 f_sleep(0.5);
2228 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002229
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002230 /* Send RTP packets to connection #1, receive on connection #0 */
2231 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2232 f_sleep(0.5);
2233 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2234 f_sleep(1.0);
2235 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2236 f_sleep(0.5);
2237 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2238
2239 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002240 f_flow_delete(RTPEM[0]);
2241 f_flow_delete(RTPEM[1], ep, call_id);
2242
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002243 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002244 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2245 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002246 f_rtpem_stats_err_check(stats[0]);
2247 f_rtpem_stats_err_check(stats[1]);
2248
2249 setverdict(pass);
2250 }
2251
Philipp Maier882843d2020-05-25 15:33:13 +02002252 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2253 * functions are real world AMR RTP payloads including AMR header. The
2254 * payloads were extracted from a trace with known good payloads. */
2255
Philipp Maier4f764ce2019-03-07 10:54:10 +01002256 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002257 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002258 }
2259
2260 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2261 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2262 }
2263
2264 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2265 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2266 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002267
Harald Weltee636afd2017-09-17 16:24:09 +08002268 /* TODO: Double-DLCX (no retransmission) */
2269
2270
2271
2272 /* TODO: AUEP (various) */
2273 /* TODO: RSIP (various) */
2274 /* TODO: RQNT (various) */
2275 /* TODO: EPCF (various) */
2276 /* TODO: AUCX (various) */
2277 /* TODO: invalid verb (various) */
2278
Oliver Smith021141e2019-06-25 12:09:01 +02002279
2280 testcase TC_conn_timeout() runs on dummy_CT {
2281 var RtpFlowData flow;
2282 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2283 var MgcpCallId call_id := '1225'H;
2284 var MGCP_RecvFrom mrf;
2285
2286 f_init(ep);
2287 log("Setting conn-timeout to 1s");
2288 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2289
2290 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002291 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002292 flow.em.portnr := 10000;
2293 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2294 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2295 f_sleep(1.5);
2296
2297 log("Stopping for 0.5s and resuming");
2298 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2299 f_sleep(0.5);
2300 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2301 f_sleep(0.1);
2302
2303 log("Stopping for 1.5s, expecting to run into timeout");
2304 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2305 f_sleep(1.5);
2306
2307 log("Resuming should fail now");
2308 f_rtpem_conn_refuse_expect(RTPEM[0]);
2309 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2310 f_sleep(0.2);
2311 f_rtpem_conn_refuse_verify(RTPEM[0]);
2312
2313 setverdict(pass);
2314 }
2315
Philipp Maier2609c752020-07-08 12:38:09 +02002316 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2317 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2318 var template MgcpCommand cmd;
2319 var MgcpResponse resp;
2320 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2321 var MgcpCallId call_id := '8376F297'H;
2322
2323 f_init(ep);
2324
2325 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2326 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2327
2328 f_dlcx_ok(ep);
2329
2330 setverdict(pass);
2331 }
2332
2333 /* Test what happens when overlapping endpoints are selected (E1) */
2334 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2335 var template MgcpCommand cmd;
2336 var MgcpResponse resp;
2337 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2338 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2339 var MgcpCallId call_id_1 := '8376F297'H;
2340 var MgcpCallId call_id_2 := '837AF2A7'H;
2341
2342 f_init();
2343
2344 /* ep_1 and ep_2 are overlapping, selecting both one after
2345 * another should work fine: */
2346 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2347 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2348 f_dlcx_ok(ep_1);
2349 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2350 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2351 f_dlcx_ok(ep_2);
2352
2353 /* When ep_1 is serving a call we can not select ep_2 becaus
2354 * it is overlapping with ep_1 */
2355 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2356 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2357 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2358 resp := mgcp_transceive_mgw(cmd, ?);
2359 if (resp.line.code != "501") {
2360 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2361 }
2362 f_dlcx_ok(ep_1);
2363
2364 setverdict(pass);
2365 }
2366
2367 /* Create one connection in loopback mode, test if the RTP packets are
2368 * actually reflected */
2369 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2370 var RtpFlowData flow;
2371 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2372 var MgcpCallId call_id := '12250989'H;
2373 var RtpemStats stats;
2374
2375 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002376 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002377 flow.em.portnr := 10000;
2378 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2379
2380 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2381 f_sleep(1.0);
2382 f_flow_delete(RTPEM[0], ep, call_id);
2383
2384 stats := f_rtpem_stats_get(RTPEM[0]);
2385
2386 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2387 setverdict(fail);
2388 }
2389 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2390 setverdict(fail);
2391 }
2392
2393 f_rtpem_stats_err_check(stats);
2394
2395 setverdict(pass);
2396 }
2397
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002398 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2399 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2400 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2401 var template MgcpCommand cmd;
2402 var MgcpResponse resp;
2403 var MgcpCallId call_id := '1234'H;
2404 var MgcpConnectionId conn_id;
2405
2406 f_init(ep);
2407
2408 /* create the connection on the MGW */
2409 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2410 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2411 conn_id := extract_conn_id(resp);
2412
2413 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2414 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2415 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2416 valueof(ts_SDP_ptime(20)) });
2417 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2418
2419 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2420 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2421 }
2422 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2423 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2424 }
2425
2426 /* clean-up */
2427 f_dlcx_ok(ep, call_id);
2428 setverdict(pass);
2429 }
2430
2431 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2432 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2433 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2434 var template MgcpCommand cmd;
2435 var MgcpResponse resp;
2436 var MgcpCallId call_id := '1234'H;
2437 var MgcpConnectionId conn_id;
2438
2439 f_init(ep);
2440
2441 /* create the connection on the MGW */
2442 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2443 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2444 conn_id := extract_conn_id(resp);
2445
2446 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2447 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2448 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2449 valueof(ts_SDP_ptime(20)) });
2450 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2451
2452 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2453 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2454 }
2455 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2456 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2457 }
2458
2459 /* clean-up */
2460 f_dlcx_ok(ep, call_id);
2461 setverdict(pass);
2462 }
2463
Harald Welte00a067f2017-09-13 23:27:17 +02002464 control {
2465 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002466 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002467 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002468 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002469 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002470 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002471 execute(TC_crcx_early_bidir_mode());
2472 execute(TC_crcx_unsupp_param());
2473 execute(TC_crcx_missing_callid());
2474 execute(TC_crcx_missing_mode());
2475 execute(TC_crcx_unsupp_packet_intv());
2476 execute(TC_crcx_illegal_double_lco());
2477 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002478 execute(TC_crcx_wildcarded());
2479 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002480 execute(TC_mdcx_without_crcx());
2481 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02002482 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002483 execute(TC_mdcx_wildcarded());
2484 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002485 execute(TC_crcx_and_dlcx_ep_callid_connid());
2486 execute(TC_crcx_and_dlcx_ep_callid());
2487 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002488 execute(TC_crcx_and_dlcx_ep_callid_inval());
2489 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002490 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002491
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002492 execute(TC_crcx_osmux_wildcard());
2493 execute(TC_crcx_osmux_fixed());
2494 execute(TC_crcx_osmux_fixed_twice());
2495 execute(TC_one_crcx_receive_only_osmux());
2496 execute(TC_one_crcx_loopback_osmux());
2497 execute(TC_two_crcx_and_rtp_osmux());
2498 execute(TC_two_crcx_and_rtp_osmux_bidir());
2499 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2500 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2501
Harald Welte33d82162017-12-28 03:21:57 +01002502 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002503
2504 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002505
2506 execute(TC_one_crcx_receive_only_rtp());
2507 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002508 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02002509 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002510 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002511 execute(TC_two_crcx_diff_pt_and_rtp());
2512 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002513 execute(TC_two_crcx_mdcx_and_rtp());
2514 execute(TC_two_crcx_and_unsolicited_rtp());
2515 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002516 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002517 execute(TC_amr_oa_bwe_rtp_conversion());
2518 execute(TC_amr_oa_oa_rtp_conversion());
2519 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002520
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002521 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002522
2523 execute(TC_e1_crcx_and_dlcx_ep());
2524 execute(TC_e1_crcx_with_overlap());
2525 execute(TC_e1_crcx_loopback());
2526
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002527 execute(TC_crcx_mdcx_ip4());
2528 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002529 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2530 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2531 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2532 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2533 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2534 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2535 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2536 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02002537
2538 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
2539 * older versions of osmo-mgw. This eventually leads into
2540 * a failure of all subsequent testcases, so it is important
2541 * not to add new testcaes after this one. */
2542 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02002543 }
2544}