blob: e1d713e1542a32b8cc798bd6c0779e0927bf24d2 [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;
Philipp Maier3560bd62021-08-19 11:52:23 +020037 import from Osmocom_CTRL_Functions all;
38 import from Osmocom_CTRL_Types all;
39 import from Osmocom_CTRL_Adapter all;
Harald Welte00a067f2017-09-13 23:27:17 +020040
Philipp Maierbb7a01c2018-02-01 12:32:57 +010041 const charstring c_mgw_domain := "mgw";
42 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
43
Harald Welte21ba5572017-09-19 17:55:05 +080044 /* any variables declared in the component will be available to
45 * all functions that 'run on' the named component, similar to
46 * class members in C++ */
Philipp Maier3560bd62021-08-19 11:52:23 +020047 type component dummy_CT extends StatsD_ConnHdlr, CTRL_Adapter_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080048 port MGCP_CODEC_PT MGCP;
49 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010050 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080051 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010052
Philipp Maier2321ef92018-06-27 17:52:04 +020053 var RTP_Emulation_CT vc_RTPEM[3];
54 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010055
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020056 var OSMUX_Emulation_CT vc_OsmuxEM;
57 port OsmuxEM_CTRL_PT OsmuxEM;
58
Philipp Maier6137c322019-02-20 16:13:41 +010059 port TELNETasp_PT MGWVTY;
Philipp Maier55b90542021-07-02 12:33:19 +020060
61 var StatsD_Checker_CT vc_STATSD;
Harald Welte00a067f2017-09-13 23:27:17 +020062 };
63
Harald Weltee1e18c52017-09-17 16:23:07 +080064 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
65 var MgcpTransId tid := int2str(g_trans_id);
66 g_trans_id := g_trans_id + 1;
67 return tid;
68 }
69
Harald Welte21ba5572017-09-19 17:55:05 +080070 /* all parameters declared here can be modified / overridden by
71 * the config file in the [MODULE_PARAMETERS] section. If no
72 * config file is used or the file doesn't specify them, the
73 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080074 modulepar {
75 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020076 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020077 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080078 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020079 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020080 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010081 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020082 PortNumber mp_local_osmux_port := 1985;
Philipp Maier55b90542021-07-02 12:33:19 +020083 PortNumber mp_mgw_statsd_port := 8125;
Philipp Maier3560bd62021-08-19 11:52:23 +020084 PortNumber mp_mgw_ctrl_port := 4267;
Harald Welte3c6ebb92017-09-16 00:56:57 +080085 }
86
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020087 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
88 /* Turn on conversion mode */
89 f_vty_enter_config(MGWVTY);
90 f_vty_transceive(MGWVTY, "mgcp");
91 if (osmux_on) {
92 f_vty_transceive(MGWVTY, "osmux on");
93 } else {
94 f_vty_transceive(MGWVTY, "osmux off");
95 }
96 f_vty_transceive(MGWVTY, "exit");
97 f_vty_transceive(MGWVTY, "exit");
98
99 }
100
101 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +0100102 map(self:MGWVTY, system:MGWVTY);
103 f_vty_set_prompts(MGWVTY);
104 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200105
106 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +0100107 }
108
Harald Weltebb7523b2018-03-29 08:52:01 +0200109 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
110 runs on dummy_CT {
111 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
112 map(comp_ref:RTP, system:RTP);
113 map(comp_ref:RTCP, system:RTCP);
114 comp_ref.start(RTP_Emulation.f_main());
115 }
116
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200117 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
118 runs on dummy_CT {
119 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
120 map(comp_ref:OSMUX, system:OSMUX);
121 comp_ref.start(OSMUX_Emulation.f_main());
122 }
123
Harald Welte21ba5572017-09-19 17:55:05 +0800124 /* initialization function, called by each test case at the
125 * beginning, but 'initialized' variable ensures its body is
126 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200127 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800128 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100129 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200130
Harald Welteedc45c12017-11-18 19:15:05 +0100131 if (initialized == false) {
132 initialized := true;
133
134 /* some random number for the initial transaction id */
135 g_trans_id := float2int(rnd()*65535.0);
136 map(self:MGCP, system:MGCP_CODEC_PT);
137 /* connect the MGCP test port using the given
138 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
139 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200140 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 +0200141 if (not ispresent(res.connId)) {
142 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200143 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200144 }
Harald Welteedc45c12017-11-18 19:15:05 +0100145 g_mgcp_conn_id := res.connId;
146
Harald Weltebb7523b2018-03-29 08:52:01 +0200147 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
148 f_rtpem_init(vc_RTPEM[i], i);
149 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
150 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200151 if (osmux_on) {
152 f_osmuxem_init(vc_OsmuxEM);
153 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
154 }
Philipp Maier55b90542021-07-02 12:33:19 +0200155
Philipp Maier2ff3e662021-08-19 10:52:33 +0200156 f_init_statsd("VirtCallAgent", vc_STATSD, mp_local_ipv4, mp_mgw_statsd_port);
Philipp Maier55b90542021-07-02 12:33:19 +0200157 connect(self:STATSD_PROC, vc_STATSD:STATSD_PROC);
158
159 f_statsd_reset();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800160 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800161
Philipp Maier3560bd62021-08-19 11:52:23 +0200162 f_ipa_ctrl_start_client(mp_remote_ipv4, mp_mgw_ctrl_port);
163
Harald Welteedc45c12017-11-18 19:15:05 +0100164 if (isvalue(ep)) {
165 /* do a DLCX on all connections of the EP */
166 f_dlcx_ignore(valueof(ep));
167 }
Philipp Maier6137c322019-02-20 16:13:41 +0100168
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200169 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800170 }
171
Harald Welte00a067f2017-09-13 23:27:17 +0200172 testcase TC_selftest() runs on dummy_CT {
173 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 +0100174 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 +0200175 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
176 "I: 1\n" &
177 "\n" &
178 "v=0\r\n" &
179 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
180 "s=-\r\n" &
181 "c=IN IP4 0.0.0.0\r\n" &
182 "t=0 0\r\n" &
183 "m=audio 0 RTP/AVP 126\r\n" &
184 "a=rtpmap:126 AMR/8000\r\n" &
185 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100186 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 +0200187 "M: sendrecv\r" &
188 "C: 2\r\n" &
189 "I: 1\r\n" &
190 "L: p:20, a:AMR, nt:IN\r\n" &
191 "\n" &
192 "v=0\r\n" &
193 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800194 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200195 "c=IN IP4 0.0.0.0\r\n" &
196 "t=0 0\r\n" &
197 "m=audio 4441 RTP/AVP 99\r\n" &
198 "a=rtpmap:99 AMR/8000\r\n" &
199 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800200 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200201
202 log(c_auep);
203 log(dec_MgcpCommand(c_auep));
204
205 log(c_mdcx3);
206 log(dec_MgcpCommand(c_mdcx3));
207
208 log(c_mdcx3_ret);
209 log(dec_MgcpResponse(c_mdcx3_ret));
210
211 log(c_mdcx4);
212 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800213
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100214 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
215 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 +0800216
217 log(c_crcx510_ret);
218 log(dec_MgcpResponse(c_crcx510_ret));
219 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100220
221 /* We didn't encounter any DTE, so pass the test */
222 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800223 }
224
Harald Weltee636afd2017-09-17 16:24:09 +0800225 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100226 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800227 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
228 * - CRCX with remote session description and without
229 *
230 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100231 * x packetization != 20ms
232 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800233 * x unsupported mode (517)
234 * x bidirectional mode before RemoteConnDesc: 527
235 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100236 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800237 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
238 */
239
Harald Welte21ba5572017-09-19 17:55:05 +0800240 /* build a receive template for receiving a MGCP message. You
241 * pass the MGCP response template in, and it will generate an
242 * MGCP_RecvFrom template that can match the primitives arriving on the
243 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800244 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
245 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100246 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200247 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800248 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200249 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800250 locPort := mp_local_udp_port,
251 msg := { response := resp }
252 }
253 return mrf;
254 }
255
256 /* Send a MGCP request + receive a (matching!) response */
257 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
258 var MgcpMessage msg := { command := valueof(cmd) };
259 resp.line.trans_id := cmd.line.trans_id;
260 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800261 var MGCP_RecvFrom mrf;
262 timer T := 5.0;
263
Harald Welte55015362017-11-18 16:02:42 +0100264 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800265 T.start;
266 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800267 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200268 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
269 setverdict(fail, "Response didn't match template");
270 mtc.stop;
271 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800272 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200273 [] T.timeout {
274 setverdict(fail, "Timeout waiting for response to ", cmd);
275 mtc.stop;
276 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800277 }
278 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800279
280 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
281 return mrf.msg.response;
282 } else {
283 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
284 return r;
285 }
Harald Welte00a067f2017-09-13 23:27:17 +0200286 }
287
Harald Welteba62c8c2017-11-18 18:26:49 +0100288 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
289 var integer i;
290 for (i := 0; i < lengthof(resp.params); i := i + 1) {
291 var MgcpParameter par := resp.params[i];
292 if (par.code == "I") {
293 return str2hex(par.val);
294 }
295 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200296 setverdict(fail, "Could not find conn id for MgcpReponse");
297 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100298 return '00000000'H;
299 }
300
Harald Welte10889c12017-11-18 19:40:31 +0100301 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
302 template MgcpCallId call_id := omit,
303 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100304 var template MgcpCommand cmd;
305 var MgcpResponse resp;
306 var template MgcpResponse rtmpl := {
307 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100308 code := ret_code,
309 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100310 },
311 params := *,
312 sdp := *
313 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100314 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100315 resp := mgcp_transceive_mgw(cmd, rtmpl);
316 }
317
Harald Welte10889c12017-11-18 19:40:31 +0100318 /* Send DLCX and expect OK response */
319 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
320 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100321 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100322 }
323
Harald Welteba62c8c2017-11-18 18:26:49 +0100324 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100325 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100326 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100327 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100328 }
329
Harald Weltebb7523b2018-03-29 08:52:01 +0200330 type record HostPort {
331 charstring hostname,
332 integer portnr optional
333 }
334 type record RtpFlowData {
335 HostPort em, /* emulation side */
336 HostPort mgw, /* mgw side */
337 uint7_t pt,
338 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200339 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100340 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200341 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
342 MgcpOsmuxCID osmux_cid optional,
343 MgcpOsmuxCID osmux_cid_response optional,
344 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100345 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200346 }
347
Philipp Maier2321ef92018-06-27 17:52:04 +0200348 /* Create an RTP flow (bidirectional, or receive-only) */
349 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 +0200350 boolean one_phase := true)
351 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200352 var template MgcpCommand cmd;
353 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100354 var SDP_attribute_list attributes;
355
356 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
357 if (isvalue(flow.fmtp)) {
358 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
359 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200360
361 /* bind local RTP emulation socket */
362 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
363
Philipp Maier28bb8292018-07-20 17:09:17 +0200364 /* configure rtp-emulation */
365 if (ispresent(flow.rtp_cfg)) {
366 f_rtpem_configure(pt, flow.rtp_cfg);
367 } else {
368 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
369 rtp_cfg.tx_payload_type := flow.pt
370 f_rtpem_configure(pt, rtp_cfg);
371 }
372
Harald Weltebb7523b2018-03-29 08:52:01 +0200373 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200374 /* Connect flow to MGW using a CRCX that also contains an SDP
375 * part that tells the MGW where we are listening for RTP streams
376 * that come from the MGW. We get a fully working connection in
377 * one go. */
378
379 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200380 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100381 flow.em.portnr, { int2str(flow.pt) }, attributes);
382
Harald Weltebb7523b2018-03-29 08:52:01 +0200383 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
384 flow.mgcp_conn_id := extract_conn_id(resp);
385 /* extract port number from response */
386 flow.mgw.portnr :=
387 resp.sdp.media_list[0].media_field.ports.port_number;
388 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200389 /* Create a half-open connection only. We do not tell the MGW
390 * where it can send RTP streams to us. This means this
391 * connection will only be able to receive but can not send
392 * data back to us. In order to turn the connection in a fully
393 * bi-directional one, a separate MDCX is needed. */
394
395 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200396 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
397 flow.mgcp_conn_id := extract_conn_id(resp);
398 /* extract MGW-side port number from response */
399 flow.mgw.portnr :=
400 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200401 }
402 /* finally, connect the emulation-side RTP socket to the MGW */
403 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
404 }
405
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200406 /* Create an Osmux flow (bidirectional, or receive-only) */
407 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
408 boolean one_phase := true)
409 runs on dummy_CT {
410 var template MgcpCommand cmd;
411 var MgcpResponse resp;
412 var SDP_attribute_list attributes;
413 var OsmuxTxHandle tx_hdl;
414 var OsmuxRxHandle rx_hdl;
415 var charstring cid_response;
416 var OsmuxCID cid_resp_parsed
417
418 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
419 if (isvalue(flow.fmtp)) {
420 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
421 }
422
423 /* bind local Osmux emulation socket */
424 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
425
426 /* configure osmux-emulation */
427 if (ispresent(flow.osmux_cfg)) {
428 f_osmuxem_configure(pt, flow.osmux_cfg);
429 } else {
430 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
431 f_osmuxem_configure(pt, osmux_cfg);
432 flow.osmux_cfg := osmux_cfg
433 }
434
435 if (one_phase) {
436 /* Connect flow to MGW using a CRCX that also contains an SDP
437 * part that tells the MGW where we are listening for Osmux streams
438 * that come from the MGW. We get a fully working connection in
439 * one go. */
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200440 if (flow.osmux_cid != -1) {
441 /* We may still want to negotiate osmux CID later at MDCX */
442 rx_hdl := c_OsmuxemDefaultRxHandle;
443 rx_hdl.cid := flow.osmux_cid;
444 f_osmuxem_register_rxhandle(pt, rx_hdl);
445 flow.osmux_cid_sent := true;
446 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200447 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
448 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
449 flow.em.portnr, { int2str(flow.pt) }, attributes);
450 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
451 flow.mgcp_conn_id := extract_conn_id(resp);
452 /* extract port number from response */
453 flow.mgw.portnr :=
454 resp.sdp.media_list[0].media_field.ports.port_number;
455 } else {
456 /* Create a half-open connection only. We do not tell the MGW
457 * where it can send Osmux streams to us. This means this
458 * connection will only be able to receive but can not send
459 * data back to us. In order to turn the connection in a fully
460 * bi-directional one, a separate MDCX is needed. */
461
462 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
463 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
464
465 flow.mgcp_conn_id := extract_conn_id(resp);
466 /* extract MGW-side port number from response */
467 flow.mgw.portnr :=
468 resp.sdp.media_list[0].media_field.ports.port_number;
469 }
470
471 /* extract Osmux CID we got assigned by the MGW */
472 var MgcpMessage resp_msg := {
473 response := resp
474 }
475
476 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
477 setverdict(fail, "No Osmux CID in MGCP response", resp);
478 mtc.stop;
479 }
480
481 /* Make sure response is no wildcard */
482 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
483 if (flow.osmux_cid_response == -1) {
484 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
485 mtc.stop;
486 }
487 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
488 f_osmuxem_register_txhandle(pt, tx_hdl);
489
490 /* finally, connect the emulation-side RTP socket to the MGW */
491 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
492 }
493
Philipp Maier2321ef92018-06-27 17:52:04 +0200494 /* Modify an existing RTP flow */
495 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
496 runs on dummy_CT {
497 var template MgcpCommand cmd;
498 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100499 var SDP_attribute_list attributes;
500
501 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
502 if (isvalue(flow.fmtp)) {
503 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
504 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200505
506 /* rebind local RTP emulation socket to the new address */
507 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
508
Philipp Maier28bb8292018-07-20 17:09:17 +0200509 /* reconfigure rtp-emulation */
510 if (ispresent(flow.rtp_cfg)) {
511 f_rtpem_configure(pt, flow.rtp_cfg);
512 } else {
513 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
514 rtp_cfg.tx_payload_type := flow.pt
515 f_rtpem_configure(pt, rtp_cfg);
516 }
517
Philipp Maier2321ef92018-06-27 17:52:04 +0200518 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
519 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
520 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100521 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200522 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
523
524 /* extract MGW-side port number from response. (usually this
525 * will not change, but thats is up to the MGW) */
526 flow.mgw.portnr :=
527 resp.sdp.media_list[0].media_field.ports.port_number;
528
529 /* reconnect the emulation-side RTP socket to the MGW */
530 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
531 }
532
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200533 /* Modify an existing Osmux flow */
534 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
535 runs on dummy_CT {
536 var template MgcpCommand cmd;
537 var MgcpResponse resp;
538 var SDP_attribute_list attributes;
539 var OsmuxRxHandle rx_hdl;
540 var charstring cid_response;
541 var OsmuxCID cid_resp_parsed
542
543 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
544 if (isvalue(flow.fmtp)) {
545 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
546 }
547
548 /* rebind local Osmux emulation socket to the new address */
549 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
550
551 /* configure osmux-emulation */
552 if (ispresent(flow.osmux_cfg)) {
553 f_osmuxem_configure(pt, flow.osmux_cfg);
554 } else {
555 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
556 f_osmuxem_configure(pt, osmux_cfg);
557 }
558
559 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
560 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
561 rx_hdl := c_OsmuxemDefaultRxHandle;
562 rx_hdl.cid := flow.osmux_cid;
563 f_osmuxem_register_rxhandle(pt, rx_hdl);
564 flow.osmux_cid_sent := true;
565 }
566
567 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
568 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
569 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
570 flow.em.portnr, { int2str(flow.pt) }, attributes);
571 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
572
573 /* extract MGW-side port number from response. (usually this
574 * will not change, but thats is up to the MGW) */
575 flow.mgw.portnr :=
576 resp.sdp.media_list[0].media_field.ports.port_number;
577
578 /* extract Osmux CID we got assigned by the MGW */
579 var MgcpMessage resp_msg := {
580 response := resp
581 }
582
583 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
584 setverdict(fail, "No Osmux CID in MGCP response", resp);
585 mtc.stop;
586 }
587
588 /* Make sure response is no wildcard */
589 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
590 if (cid_resp_parsed == -1) {
591 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
592 mtc.stop;
593 }
594 if (cid_resp_parsed != flow.osmux_cid_response) {
595 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
596 mtc.stop;
597 }
598
599 /* reconnect the emulation-side Osmux socket to the MGW */
600 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
601 }
602
Philipp Maier2321ef92018-06-27 17:52:04 +0200603 /* Delete an existing RTP flow */
604 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
605 runs on dummy_CT {
606 var template MgcpCommand cmd;
607 var MgcpResponse resp;
608
609 /* Switch off RTP flow */
610 f_rtpem_mode(pt, RTPEM_MODE_NONE);
611
612 /* Delete connection on MGW (if needed) */
613 if (isvalue(call_id) and isvalue(ep)) {
614 f_sleep(0.1);
615 f_dlcx_ok(valueof(ep), call_id);
616 }
617 }
618
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200619 /* Delete an existing Osmux flow */
620 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
621 runs on dummy_CT {
622 var template MgcpCommand cmd;
623 var MgcpResponse resp;
624
625 /* Switch off Osmux flow */
626 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
627
628 /* Delete connection on MGW (if needed) */
629 if (isvalue(call_id) and isvalue(ep)) {
630 f_sleep(0.1);
631 f_dlcx_ok(valueof(ep), call_id);
632 }
633 }
634
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100635 function f_crcx(charstring ep_prefix) runs on dummy_CT {
636 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800637 var template MgcpCommand cmd;
638 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100639 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800640
Harald Welteedc45c12017-11-18 19:15:05 +0100641 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800642
Harald Welteba62c8c2017-11-18 18:26:49 +0100643 /* create the connection on the MGW */
644 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100645 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100646 extract_conn_id(resp);
647
648 /* clean-up */
649 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100650 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100651
Philipp Maier45635f42018-06-05 17:28:02 +0200652 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
653 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
654 var template MgcpCommand cmd;
655 var MgcpResponse resp;
656 var MgcpCallId call_id := '1234'H;
657
658 f_init(ep);
659
660 /* create the connection on the MGW */
661 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
662 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
663 extract_conn_id(resp);
664
665 /* clean-up */
666 f_dlcx_ok(ep, call_id);
667
668 /* See also OS#2658: Even when we omit the LCO information, we
669 expect the MGW to pick a sane payload type for us. This
670 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200671 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200672 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200673 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200674 }
675
676 /* See also OS#2658: We also expect the MGW to assign a port
677 number to us. */
678 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
679 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200680 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200681 }
682 }
683
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200684 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
685 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
686 var template MgcpCommand cmd;
687 var MgcpResponse resp;
688 var MgcpCallId call_id := '1234'H;
689 var charstring cid_response;
690
691 if (run_init) {
692 f_init(ep, true);
693 }
694
695 /* create the connection on the MGW */
696 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
697 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
698 extract_conn_id(resp);
699
700 /* extract Osmux CID we got assigned by the MGW */
701 var MgcpMessage resp_msg := {
702 response := resp
703 }
704
705 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
706 setverdict(fail, "No Osmux CID in MGCP response", resp);
707 mtc.stop;
708 }
709
710 /* Make sure response is no wildcard */
711 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
712 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
713 mtc.stop;
714 }
715
716 /* clean-up */
717 f_dlcx_ok(ep, call_id);
718 }
719
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100720 /* test valid CRCX without SDP */
721 testcase TC_crcx() runs on dummy_CT {
722 f_crcx(c_mgw_ep_rtpbridge);
723 setverdict(pass);
724 }
725
Philipp Maier45635f42018-06-05 17:28:02 +0200726 /* test valid CRCX without SDP and LCO */
727 testcase TC_crcx_no_lco() runs on dummy_CT {
728 f_crcx_no_lco(c_mgw_ep_rtpbridge);
729 setverdict(pass);
730 }
731
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100732 /* test valid CRCX without SDP (older method without endpoint prefix) */
733 testcase TC_crcx_noprefix() runs on dummy_CT {
734 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800735 setverdict(pass);
736 }
737
738 /* test CRCX with unsupported mode, expect 517 */
739 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
740 var template MgcpCommand cmd;
741 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100742 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100743 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800744 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
745
Harald Welteedc45c12017-11-18 19:15:05 +0100746 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800747
Harald Welteba62c8c2017-11-18 18:26:49 +0100748 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800749 resp := mgcp_transceive_mgw(cmd, rtmpl);
750 setverdict(pass);
751 }
752
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200753 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
754 testcase TC_crcx_osmo_ign() runs on dummy_CT {
755 var template MgcpCommand cmd;
756 var MgcpResponse resp;
757 var MgcpEndpoint ep := "7@" & c_mgw_domain;
758 var MgcpCallId call_id := '3'H;
759
760 f_init(ep);
761
762 /* CRCX 1 7@mgw MGCP 1.0
763 C: 3
764 L: p:20, a:GSM-EFR, nt:IN
765 M: recvonly
766 X-Osmo-IGN: C
767 */
768
769 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
770 cmd.params := {ts_MgcpParCallId(call_id),
771 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
772 t_MgcpParConnMode("recvonly"),
773 t_MgcpParOsmoIGN("C")};
774 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
775 extract_conn_id(resp);
776
777 /* clean-up */
778 f_dlcx_ok(ep, call_id);
779 setverdict(pass);
780 }
781
Harald Welte21ba5572017-09-19 17:55:05 +0800782 /* test CRCX with early bi-directional mode, expect 527 as
783 * bi-diretional media can only be established once both local and
784 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800785 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
786 var template MgcpCommand cmd;
787 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100788 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100789 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800790 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
791
Harald Welteedc45c12017-11-18 19:15:05 +0100792 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800793
Harald Welteba62c8c2017-11-18 18:26:49 +0100794 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800795 resp := mgcp_transceive_mgw(cmd, rtmpl);
796 setverdict(pass);
797 }
798
799 /* test CRCX with unsupported Parameters */
800 testcase TC_crcx_unsupp_param() runs on dummy_CT {
801 var template MgcpCommand cmd;
802 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100803 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100804 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800805 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
806
Harald Welteedc45c12017-11-18 19:15:05 +0100807 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800808
Harald Welteba62c8c2017-11-18 18:26:49 +0100809 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100810 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
811 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
812
Harald Weltee636afd2017-09-17 16:24:09 +0800813 resp := mgcp_transceive_mgw(cmd, rtmpl);
814 setverdict(pass);
815 }
816
817 /* test CRCX with missing CallId */
818 testcase TC_crcx_missing_callid() runs on dummy_CT {
819 var template MgcpCommand cmd;
820 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100821 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100822 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800823
Harald Welteedc45c12017-11-18 19:15:05 +0100824 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800825
Harald Welteba62c8c2017-11-18 18:26:49 +0100826 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800827 cmd.params := {
828 t_MgcpParConnMode("recvonly"),
829 t_MgcpParLocConnOpt("p:20")
830 }
831 resp := mgcp_transceive_mgw(cmd, rtmpl);
832 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100833
Harald Weltee636afd2017-09-17 16:24:09 +0800834 }
835
836 /* test CRCX with missing Mode */
837 testcase TC_crcx_missing_mode() runs on dummy_CT {
838 var template MgcpCommand cmd;
839 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100840 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100841 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100842 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800843
Harald Welteedc45c12017-11-18 19:15:05 +0100844 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800845
Harald Welteba62c8c2017-11-18 18:26:49 +0100846 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800847 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100848 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800849 t_MgcpParLocConnOpt("p:20")
850 }
851 resp := mgcp_transceive_mgw(cmd, rtmpl);
852 setverdict(pass);
853 }
854
855 /* test CRCX with unsupported packetization interval */
856 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
857 var template MgcpCommand cmd;
858 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100859 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100860 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100861 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800862
Harald Welteedc45c12017-11-18 19:15:05 +0100863 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800864
Harald Welteba62c8c2017-11-18 18:26:49 +0100865 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100866 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800867 resp := mgcp_transceive_mgw(cmd, rtmpl);
868 setverdict(pass);
869 }
870
871 /* test CRCX with illegal double presence of local connection option */
872 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
873 var template MgcpCommand cmd;
874 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100875 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100876 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800877 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
878
Harald Welteedc45c12017-11-18 19:15:05 +0100879 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800880
Harald Welteba62c8c2017-11-18 18:26:49 +0100881 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100882 /* p:20 is permitted only once and not twice! */
883 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800884 resp := mgcp_transceive_mgw(cmd, rtmpl);
885 setverdict(pass);
886 }
887
888 /* test valid CRCX with valid SDP */
889 testcase TC_crcx_sdp() runs on dummy_CT {
890 var template MgcpCommand cmd;
891 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100892 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100893 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800894
Harald Welteedc45c12017-11-18 19:15:05 +0100895 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800896
Harald Welteba62c8c2017-11-18 18:26:49 +0100897 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800898 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
899 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
900 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100901 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100902
903 /* clean-up */
904 f_dlcx_ok(ep, call_id);
905
Harald Weltee636afd2017-09-17 16:24:09 +0800906 setverdict(pass);
907 }
908
Philipp Maier5e06cee2018-02-01 18:28:08 +0100909 /* test valid wildcarded CRCX */
910 testcase TC_crcx_wildcarded() runs on dummy_CT {
911 var template MgcpCommand cmd;
912 var MgcpResponse resp;
913 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
914 var MgcpCallId call_id := '1234'H;
915 var MgcpEndpoint ep_assigned;
916 f_init();
917
918 /* create the connection on the MGW */
919 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
920 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
921 extract_conn_id(resp);
922
923 /* extract endpoint name we got assigned by the MGW */
924 var MgcpMessage resp_msg := {
925 response := resp
926 }
927 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
928 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200929 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100930 }
931
932 /* clean-up */
933 f_dlcx_ok(ep_assigned, call_id);
934
935 setverdict(pass);
936 }
937
938 /* test valid wildcarded CRCX */
939 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maierc60e8472020-07-08 12:57:13 +0200940 const integer n_endpoints := 31;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100941 var integer i;
942 var template MgcpCommand cmd;
943 var MgcpResponse resp;
944 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
945 var MgcpCallId call_id := '1234'H;
946 var MgcpEndpoint ep_assigned[n_endpoints];
947 f_init();
948
949 /* Exhaust all endpoint resources on the virtual trunk */
950 for (i := 0; i < n_endpoints; i := i+1) {
951 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
952 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
953
954 /* Make sure we got a connection id */
955 extract_conn_id(resp);
956
957 var MgcpMessage resp_msg := {
958 response := resp
959 }
960 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
961 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200962 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100963 }
964 }
965
966 /* Try to allocate one more endpoint, which should fail */
967 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
968 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
969 resp := mgcp_transceive_mgw(cmd, rtmpl);
970 setverdict(pass);
971
972 /* clean-up */
973 for (i := 0; i < n_endpoints; i := i+1) {
974 f_dlcx_ok(ep_assigned[i], call_id);
975 }
976 setverdict(pass);
977 }
978
Harald Weltee636afd2017-09-17 16:24:09 +0800979 /* TODO: various SDP related bits */
980
981
982 /* TODO: CRCX with X-Osmux */
983 /* TODO: double CRCX without force_realloc */
984
985 /* TODO: MDCX (various) */
986
987 /* TODO: MDCX without CRCX first */
988 testcase TC_mdcx_without_crcx() runs on dummy_CT {
989 var template MgcpCommand cmd;
990 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100991 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100992 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800993 var template MgcpResponse rtmpl := {
994 line := {
995 /* TODO: accept/enforce better error? */
996 code := "400",
997 string := ?
998 },
999 params:= { },
1000 sdp := omit
1001 };
1002
Harald Welteedc45c12017-11-18 19:15:05 +01001003 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001004
Harald Welte2bcfd3a2017-11-18 22:14:35 +01001005 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001006 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1007 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1008 valueof(ts_SDP_ptime(20)) });
1009 resp := mgcp_transceive_mgw(cmd, rtmpl);
1010 setverdict(pass);
1011 }
1012
1013 /* DLCX without CRCX first */
1014 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1015 var template MgcpCommand cmd;
1016 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001017 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001018 var template MgcpResponse rtmpl := {
1019 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001020 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001021 string := ?
1022 },
1023 params:= { },
1024 sdp := omit
1025 };
1026
Harald Welteedc45c12017-11-18 19:15:05 +01001027 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001028
Harald Welteedc45c12017-11-18 19:15:05 +01001029 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001030 resp := mgcp_transceive_mgw(cmd, rtmpl);
1031 setverdict(pass);
1032 }
1033
Philipp Maier21c1cff2021-07-20 14:22:53 +02001034 /* DLCX to non existing endpoint */
1035 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1036 var template MgcpCommand cmd;
1037 var MgcpResponse resp;
1038 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "AA@" & c_mgw_domain;
1039 var template MgcpResponse rtmpl := {
1040 line := {
1041 code := ("500"),
1042 string := ?
1043 },
1044 params:= { },
1045 sdp := omit
1046 };
1047
1048 f_init(ep);
1049
1050 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1051 resp := mgcp_transceive_mgw(cmd, rtmpl);
1052 setverdict(pass);
1053 }
1054
Philipp Maier8a3dc922018-02-02 14:55:12 +01001055 /* test valid wildcarded MDCX */
1056 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1057 /* Note: A wildcarded MDCX is not allowed, so we expect the
1058 * MGW to reject this request */
1059 var template MgcpCommand cmd;
1060 var MgcpResponse resp;
1061 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1062 var MgcpCallId call_id := '1225'H;
1063 var template MgcpResponse rtmpl := {
1064 line := {
1065 /* TODO: accept/enforce better error? */
1066 code := "507",
1067 string := ?
1068 },
1069 params:= { },
1070 sdp := omit
1071 };
1072
1073 f_init(ep);
1074
1075 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1076 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1077 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1078 valueof(ts_SDP_ptime(20)) });
1079 resp := mgcp_transceive_mgw(cmd, rtmpl);
1080 setverdict(pass);
1081 }
1082
1083 /* test valid wildcarded DLCX */
1084 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001085 var template MgcpCommand cmd;
1086 var MgcpResponse resp;
1087 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001088 const integer n_endpoints := 31;
1089 var integer i;
1090 var MgcpCallId call_id := '1234'H;
1091 var StatsDExpects expect;
1092 f_init(ep);
1093
1094 /* Allocate a few endpoints */
1095 for (i := 0; i < n_endpoints; i := i+1) {
1096 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1097 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1098 }
1099
Philipp Maier1298b092021-11-18 18:33:39 +01001100 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
1101 * occupied endpoints */
1102 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001103 expect := {
1104 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := n_endpoints, max := n_endpoints}
1105 };
1106 f_statsd_expect(expect);
1107
1108 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001109 var template MgcpResponse rtmpl := {
1110 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001111 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001112 string := ?
1113 },
1114 params:= { },
1115 sdp := omit
1116 };
Philipp Maier55b90542021-07-02 12:33:19 +02001117 cmd := ts_DLCX(get_next_trans_id(), ep);
1118 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001119
Philipp Maier55b90542021-07-02 12:33:19 +02001120 /* The second interval must resturn a result with 0 endpoints in use. */
Philipp Maier1298b092021-11-18 18:33:39 +01001121 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001122 expect := {
1123 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1124 };
1125 f_statsd_expect(expect);
1126
Philipp Maier8a3dc922018-02-02 14:55:12 +01001127 setverdict(pass);
1128 }
1129
Harald Welte79181ff2017-11-18 19:26:11 +01001130 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1131 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1132 var template MgcpCommand cmd;
1133 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001134 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001135 var MgcpCallId call_id := '51234'H;
1136
1137 f_init(ep);
1138
1139 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1140 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1141
1142 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1143
1144 setverdict(pass);
1145 }
1146
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001147 /* test valid CRCX without SDP */
1148 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1149 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1150 setverdict(pass);
1151 }
1152
1153 /* test valid CRCX without SDP */
1154 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1155 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1156 setverdict(pass);
1157 }
1158
1159 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1160 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1161 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1162 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1163 setverdict(pass);
1164 }
1165
1166 /* Create one half open connection in receive-only mode. The MGW must accept
1167 * the packets but must not send any. */
1168 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1169 var RtpFlowData flow;
1170 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1171 var MgcpCallId call_id := '1225'H;
1172 var OsmuxemStats stats;
1173 var OsmuxTxHandle tx_hdl;
1174
1175 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001176 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001177 flow.em.portnr := mp_local_osmux_port;
1178 flow.osmux_cid := -1;
1179 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1180
1181 /* create a transmitter not yet known by MGW */
1182 tx_hdl := valueof(t_TxHandleAMR590(2));
1183 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1184
1185 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1186 f_sleep(1.0);
1187 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1188
1189 stats := f_osmuxem_stats_get(OsmuxEM);
1190
1191 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1192 setverdict(fail);
1193 }
1194 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1195 setverdict(fail);
1196 }
1197
1198 f_osmuxem_stats_err_check(stats);
1199
1200 setverdict(pass);
1201 }
1202
1203 /* Create one connection in loopback mode, test if the Osmux packets are
1204 * actually reflected */
1205 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1206 var RtpFlowData flow;
1207 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1208 var MgcpCallId call_id := '1225'H;
1209 var OsmuxemStats stats;
1210 var OsmuxTxHandle tx_hdl;
1211
1212 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001213 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001214 flow.em.portnr := mp_local_osmux_port;
1215 flow.osmux_cid := 2;
1216 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1217
1218 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1219 f_sleep(1.0);
1220
1221 /* Switch off both Tx, wait to receive delayed frames from MGW */
1222 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1223 f_sleep(0.1);
1224 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1225
1226 stats := f_osmuxem_stats_get(OsmuxEM);
1227
1228 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1229 setverdict(fail);
1230 }
1231 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1232 setverdict(fail);
1233 }
1234
1235 f_osmuxem_stats_err_check(stats);
1236
1237 setverdict(pass);
1238 }
1239
1240 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1241 * must match the reception statistics on the other side and vice versa. The
1242 * user may also supply a tolerance value (number of packets) when deviations
1243 * are acceptable */
1244 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1245 var integer plen;
1246
1247 log("stats A: ", a);
1248 log("stats B: ", b);
1249 log("tolerance: ", tolerance, " packets");
1250 log("batch_size: ", batch_size, " packets");
1251
1252 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1253
1254 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1255 return false;
1256 }
1257
1258 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1259 return false;
1260 }
1261
1262 if(a.num_pkts_tx > 0) {
1263 plen := a.bytes_payload_tx / a.num_pkts_tx;
1264 } else {
1265 plen := 0;
1266 }
1267
1268 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1269 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1270 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1271 return false;
1272 }
1273
1274 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) {
1275 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1276 return false;
1277 }
1278
1279 return true;
1280 }
1281
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001282 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1283 charstring local_ip_rtp, charstring remote_ip_rtp,
1284 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001285 var RtpFlowData flow[2];
1286 var RtpemStats stats_rtp;
1287 var OsmuxemStats stats_osmux;
1288 var MgcpResponse resp;
1289 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1290 var MgcpCallId call_id := '1226'H;
1291 var integer tolerance := 0;
1292
1293 f_init(ep, true);
1294
1295 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001296 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001297 flow[0].rtp_cfg := c_RtpemDefaultCfg
1298 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1299 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1300 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);
1301 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1302 /* bind local RTP emulation sockets */
1303 flow[0].em.portnr := 10000;
1304 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1305
1306 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001307 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001308 flow[1].em.portnr := mp_local_osmux_port;
1309 flow[1].osmux_cid := 2;
1310 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1311 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1312
1313 if (bidir) {
1314 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1315 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1316
1317 /* Note: When we test bidirectional we may
1318 * loose packets during switch off because
1319 * both ends are transmitting and we only
1320 * can switch them off one by one. */
1321 tolerance := 3;
1322 } else {
1323 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1324 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1325 }
1326
1327 f_sleep(1.0);
1328
1329 /* Switch off both Tx, wait to receive delayed frames from MGW */
1330 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1331 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1332 f_sleep(0.1);
1333
1334 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1335 f_flow_delete(RTPEM[1]);
1336
1337 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1338 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1339 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1340 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1341 mtc.stop;
1342 }
1343
1344 f_rtpem_stats_err_check(stats_rtp);
1345 f_osmuxem_stats_err_check(stats_osmux);
1346
1347 setverdict(pass);
1348 }
1349
1350 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1351 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001352 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1353 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001354 }
1355
1356 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1357 * exchange some data in both directions */
1358 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001359 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1360 mp_local_ipv4, mp_remote_ipv4);
1361 }
1362
1363 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1364 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1365 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1366 mp_local_ipv6, mp_remote_ipv6);
1367 }
1368 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1369 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1370 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1371 mp_local_ipv6, mp_remote_ipv6);
1372 }
1373 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1374 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1375 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1376 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001377 }
1378
1379
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001380 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1381 charstring local_ip_rtp, charstring remote_ip_rtp,
1382 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001383 var RtpFlowData flow[2];
1384 var RtpemStats stats_rtp;
1385 var OsmuxemStats stats_osmux;
1386 var MgcpResponse resp;
1387 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1388 var MgcpCallId call_id := '1227'H;
1389 var integer num_pkts_tx[2];
1390 var integer temp;
1391
1392 f_init(ep, true);
1393
1394 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001395 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001396 flow[0].rtp_cfg := c_RtpemDefaultCfg
1397 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1398 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1399 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);
1400 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1401 /* bind local RTP emulation sockets */
1402 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001403 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001404
1405
1406 /* Create the second connection. This connection will be also
1407 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001408 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001409 flow[1].em.portnr := mp_local_osmux_port;
1410 if (crcx_osmux_wildcard) {
1411 flow[1].osmux_cid := -1;
1412 } else {
1413 flow[1].osmux_cid := 2;
1414 }
1415 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001416 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001417
1418
1419 /* The first leg starts transmitting */
1420 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1421 f_sleep(0.5);
1422 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1423 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1424 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1425 mtc.stop;
1426 }
1427 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1428 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1429 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1430 mtc.stop;
1431 }
1432
1433 /* The second leg starts transmitting a little later */
1434 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1435 f_sleep(1.0);
1436 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1437 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1438 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1439 mtc.stop;
1440 }
1441 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1442 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1443 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1444 mtc.stop;
1445 }
1446
1447 /* The first leg will now be switched into bidirectional
1448 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001449 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1450 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1451 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001452 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1453 f_sleep(0.5);
1454 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1455 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1456 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1457 mtc.stop;
1458 }
1459 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1460 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1461 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1462 mtc.stop;
1463 }
1464
1465 /* When the second leg is switched into bidirectional mode
1466 * as well, then the MGW will connect the two together and
1467 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001468 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1469 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001470 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001471
1472 if (crcx_osmux_wildcard) {
1473 /* For now we must set same CID as the MGW recvCID,
1474 * having sendCID!=recvCID is not yet supported. */
1475 flow[1].osmux_cid := flow[1].osmux_cid_response;
1476 }
1477 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1478 f_sleep(2.0);
1479
1480 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1481 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1482
1483 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1484 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1485 log("stats_rtp: ", stats_rtp);
1486 log("stats_osmux: ", stats_osmux);
1487 log("old_rtp_tx: ", num_pkts_tx[0]);
1488 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1489 mtc.stop;
1490 }
1491
1492 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1493 if (temp > 3 or temp < -3) {
1494 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1495 mtc.stop;
1496 }
1497
1498 f_rtpem_stats_err_check(stats_rtp);
1499 f_osmuxem_stats_err_check(stats_osmux);
1500
1501 /* Tear down */
1502 f_flow_delete(RTPEM[0]);
1503 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1504 setverdict(pass);
1505 }
1506
1507 /* create one RTP and one OSmux emulations and pass data in both
1508 directions. Create CRCX with wildcard Osmux CID and set it later
1509 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1510 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001511 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1512 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001513 }
1514
1515 /* create one RTP and one OSmux emulations and pass data in both
1516 directions. Create CRCX with fixed Osmux CID and keep it during
1517 MDCX. This is similar to how BSC sets up the call in AoIP. */
1518 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001519 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1520 mp_local_ipv4, mp_remote_ipv4);
1521 }
1522
1523 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1524 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1525 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1526 mp_local_ipv6, mp_remote_ipv6);
1527 }
1528 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1529 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1530 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1531 mp_local_ipv6, mp_remote_ipv6);
1532 }
1533 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1534 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1535 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1536 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001537 }
1538
Harald Welte646ecdb2017-12-28 03:21:57 +01001539 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1540 var template MgcpCommand cmd;
1541 var MgcpResponse resp;
1542
1543 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1544 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1545
1546 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1547
1548 setverdict(pass);
1549 }
1550
1551 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1552 var MgcpEndpoint ep;
1553 var MgcpCallId call_id;
1554 var integer ep_nr;
1555
1556 f_init();
1557
1558 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001559 if(ep_nr > 15) {
1560 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1561 } else {
1562 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1563 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001564 call_id := int2hex(ep_nr, 2) & '1234'H;
1565 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1566 }
1567 }
1568
Harald Welte79181ff2017-11-18 19:26:11 +01001569 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1570 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001571 var template MgcpCommand cmd;
1572 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001573 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001574 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001575
Harald Welteedc45c12017-11-18 19:15:05 +01001576 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001577
Harald Welteba62c8c2017-11-18 18:26:49 +01001578 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001579 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001580
Harald Welteba62c8c2017-11-18 18:26:49 +01001581 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001582
1583 setverdict(pass);
1584 }
1585
Harald Welte79181ff2017-11-18 19:26:11 +01001586 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1587 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1588 var template MgcpCommand cmd;
1589 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001590 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001591 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001592
1593 f_init(ep);
1594
1595 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1596 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1597
1598 f_dlcx_ok(ep);
1599
1600 setverdict(pass);
1601 }
1602
1603
Harald Welte6d167f82017-11-18 19:41:35 +01001604 /* CRCX + DLCX of valid endpoint but invalid call-id */
1605 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1606 var template MgcpCommand cmd;
1607 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001608 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001609 var MgcpCallId call_id := '51231'H;
1610
1611 f_init(ep);
1612
1613 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1614 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1615
1616 f_dlcx(ep, "516", *, 'ffff'H);
1617
1618 setverdict(pass);
1619 }
1620
1621
1622 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1623 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1624 var template MgcpCommand cmd;
1625 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001626 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001627 var MgcpCallId call_id := '51230'H;
1628
1629 f_init(ep);
1630
1631 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1632 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1633
1634 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1635
1636 setverdict(pass);
1637 }
1638
1639
Harald Weltee636afd2017-09-17 16:24:09 +08001640 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001641 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1642 var template MgcpCommand cmd;
1643 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001644 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001645 var MgcpCallId call_id := '51229'H;
1646 var template MgcpResponse rtmpl := {
1647 line := {
1648 code := "200",
1649 string := "OK"
1650 },
1651 params:= { },
1652 sdp := omit
1653 };
1654
1655 f_init(ep);
1656
1657 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1658 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1659
1660 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1661 resp := mgcp_transceive_mgw(cmd, rtmpl);
1662 resp := mgcp_transceive_mgw(cmd, rtmpl);
1663
1664 setverdict(pass);
1665 }
1666
Harald Weltebb7523b2018-03-29 08:52:01 +02001667 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1668 charstring codec) := {
1669 em := {
1670 hostname := host_a,
1671 portnr := omit
1672 },
1673 mgw := {
1674 hostname := host_b,
1675 portnr := omit
1676 },
1677 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001678 codec := codec,
1679 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001680 }
Harald Weltef53f1642017-11-18 19:57:11 +01001681
Harald Weltebb7523b2018-03-29 08:52:01 +02001682 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1683 testcase TC_rtpem_selftest() runs on dummy_CT {
1684 var RtpemStats stats[2];
1685 var integer local_port := 10000;
1686 var integer local_port2 := 20000;
1687
1688 f_init();
1689
1690 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1691 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1692
1693 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1694 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1695
1696 log("=== starting");
1697 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1698 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1699
1700 f_sleep(5.0);
1701
1702 log("=== stopping");
1703 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1704 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1705 f_sleep(0.5);
1706 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1707 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1708
1709 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1710 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1711 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1712 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001713 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001714 }
1715 setverdict(pass);
1716 }
1717
Philipp Maier2321ef92018-06-27 17:52:04 +02001718 /* Create one half open connection in receive-only mode. The MGW must accept
1719 * the packets but must not send any. */
1720 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1721 var RtpFlowData flow;
1722 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1723 var MgcpCallId call_id := '1225'H;
1724 var RtpemStats stats;
1725
1726 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001727 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001728 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001729 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001730
1731 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1732 f_sleep(1.0);
1733 f_flow_delete(RTPEM[0], ep, call_id);
1734
1735 stats := f_rtpem_stats_get(RTPEM[0]);
1736
Philipp Maier42b17cc2019-10-01 13:53:17 +02001737 /* Make sure that at least some amount of RTP packets/bytes
1738 * have has been transmitted. The compare values for
1739 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1740 * using a testrun and the results were devided by 2, so even
1741 * in load situations we should reach the minimum amount of
1742 * required packets/bytes */
1743
1744 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001745 setverdict(fail);
1746 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001747 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001748 setverdict(fail);
1749 }
Philipp Maier36291392018-07-25 09:40:44 +02001750
1751 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001752
1753 setverdict(pass);
1754 }
1755
1756 /* Create one connection in loopback mode, test if the RTP packets are
1757 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001758 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 +02001759 var RtpFlowData flow;
1760 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1761 var MgcpCallId call_id := '1225'H;
1762 var RtpemStats stats;
1763
1764 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001765 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001766 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001767 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001768
1769 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1770 f_sleep(1.0);
1771 f_flow_delete(RTPEM[0], ep, call_id);
1772
1773 stats := f_rtpem_stats_get(RTPEM[0]);
1774
1775 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1776 setverdict(fail);
1777 }
1778 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1779 setverdict(fail);
1780 }
Philipp Maier36291392018-07-25 09:40:44 +02001781
1782 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001783
1784 setverdict(pass);
1785 }
1786
Philipp Maier1ac13982021-05-07 23:06:07 +02001787 /* Create one connection in loopback mode, test if the RTP packets are
1788 * actually reflected */
1789 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001790 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001791 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001792 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1793 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1794 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001795
1796 /* Same as above, but we will intenionally not tell the MGW where to
1797 * send the outgoing traffic. The connection is still created in
1798 * loopback mode, so the MGW should take the originating address from
1799 * the incoming RTP packet and send it back to the source */
1800 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001801 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001802 }
1803
1804
Philipp Maier7df85f62018-07-25 10:26:09 +02001805 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1806 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001807 var RtpFlowData flow[2];
1808 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001809 var MgcpResponse resp;
1810 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1811 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001812 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001813
1814 f_init(ep);
1815
1816 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001817 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001818 /* bind local RTP emulation sockets */
1819 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001820 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001821
1822 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001823 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001824 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001825 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1826
1827 if (bidir) {
1828 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1829 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1830
1831 /* Note: When we test bidirectional we may
1832 * loose packets during switch off because
1833 * both ends are transmitting and we only
1834 * can switch them off one by one. */
1835 tolerance := 3;
1836 } else {
1837 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1838 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1839 }
1840
1841 f_sleep(1.0);
1842
1843 f_flow_delete(RTPEM[1]);
1844 f_flow_delete(RTPEM[0], ep, call_id);
1845
1846 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1847 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1848 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1849 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001850 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001851 }
1852
Philipp Maier36291392018-07-25 09:40:44 +02001853 f_rtpem_stats_err_check(stats[0]);
1854 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001855
Philipp Maier2321ef92018-06-27 17:52:04 +02001856 setverdict(pass);
1857 }
1858
1859 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1860 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001861 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001862 }
1863
1864 /* create two local RTP emulations; create two connections on MGW EP,
1865 * exchange some data in both directions */
1866 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001867 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1868 }
1869
1870 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1871 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1872 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1873 }
1874
1875 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1876 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1877 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001878 }
1879
1880 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001881 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1882 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001883 var RtpFlowData flow[2];
1884 var RtpemStats stats[2];
1885 var MgcpResponse resp;
1886 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1887 var MgcpCallId call_id := '1227'H;
1888 var integer num_pkts_tx[2];
1889 var integer temp;
1890
1891 f_init(ep);
1892
1893 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001894 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001895 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001896 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001897
1898 /* Create the second connection. This connection will be also
1899 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001900 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001901 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001902 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001903
1904 /* The first leg starts transmitting */
1905 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1906 f_sleep(0.5);
1907 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1908 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001909 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001910 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001911 }
1912 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1913 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001914 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001915 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001916 }
1917
1918 /* The second leg starts transmitting a little later */
1919 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1920 f_sleep(1.0);
1921 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1922 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001923 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001924 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001925 }
1926 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1927 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001928 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001929 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001930 }
1931
1932 /* The first leg will now be switched into bidirectional
1933 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001934 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1935 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1936 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001937 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1938 f_sleep(0.5);
1939 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001940 if (stats[0].num_pkts_rx_err_disabled != 0) {
1941 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001942 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001943 }
1944 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1945 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001946 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001947 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001948 }
1949
1950 /* When the second leg is switched into bidirectional mode
1951 * as well, then the MGW will connect the two together and
1952 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02001953 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1954 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001955 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001956 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1957 f_sleep(2.0);
1958
1959 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1960 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1961
1962 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1963 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001964 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001965 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001966 }
1967
1968 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1969 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001970 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001971 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001972 }
Philipp Maier36291392018-07-25 09:40:44 +02001973
1974 f_rtpem_stats_err_check(stats[0]);
1975 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001976
1977 /* Tear down */
1978 f_flow_delete(RTPEM[0]);
1979 f_flow_delete(RTPEM[1], ep, call_id);
1980 setverdict(pass);
1981 }
1982
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001983 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1984 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1985 mp_local_ipv4, mp_remote_ipv4);
1986 }
1987
1988 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
1989 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
1990 mp_local_ipv6, mp_remote_ipv6);
1991 }
1992
1993 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
1994 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1995 mp_local_ipv6, mp_remote_ipv6);
1996 }
1997
Philipp Maier2321ef92018-06-27 17:52:04 +02001998 /* Test what happens when two RTP streams from different sources target
1999 * a single connection. Is the unsolicited stream properly ignored? */
2000 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2001 var RtpFlowData flow[2];
2002 var RtpemStats stats[2];
2003 var MgcpResponse resp;
2004 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2005 var MgcpCallId call_id := '1234321326'H;
2006 var integer unsolicited_port := 10002;
2007
2008 f_init(ep);
2009
2010 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002011 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002012 /* bind local RTP emulation sockets */
2013 flow[0].em.portnr := 10000;
2014 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2015
2016 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002017 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002018 flow[1].em.portnr := 20000;
2019 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002020
2021 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2022 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2023
Philipp Maier2321ef92018-06-27 17:52:04 +02002024 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002025
Philipp Maier2321ef92018-06-27 17:52:04 +02002026 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002027 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2028 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002029 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2030
2031 f_sleep(0.5);
2032
Daniel Willmanna069d382018-12-13 13:53:33 +01002033 /* Stop transmitting packets and tear down the flows */
2034 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002035 f_flow_delete(RTPEM[0]);
2036 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002037
2038 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2039 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2040 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2041 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002042 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002043 }
2044
Philipp Maier36291392018-07-25 09:40:44 +02002045 f_rtpem_stats_err_check(stats[0]);
2046 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002047
Harald Weltebb7523b2018-03-29 08:52:01 +02002048 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002049 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002050
Philipp Maier2321ef92018-06-27 17:52:04 +02002051 /* Test a handover situation. We first create two connections transmit
2052 * some data bidirectionally. Then we will simulate a handover situation. */
2053 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2054 var RtpFlowData flow[2];
2055 var RtpemStats stats[3];
2056 var MgcpResponse resp;
2057 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2058 var MgcpCallId call_id := '76338'H;
2059 var integer port_old;
2060
2061 f_init(ep);
2062
2063 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002064 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002065 /* bind local RTP emulation sockets */
2066 flow[0].em.portnr := 10000;
2067 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2068
2069 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002070 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002071 flow[1].em.portnr := 20000;
2072 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2073
2074 /* Normal rtp flow for one second */
2075 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2076 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2077 f_sleep(1.0);
2078
2079 /* Now switch the flow over to a new port (BTS) */
2080 port_old := flow[0].em.portnr;
2081 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002082 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002083 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002084 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002085
2086 /* When handing over a call, the old source may still keep
2087 * transmitting for a while. We simulate this by injecting
2088 * some unsolicited packets on the behalf of the old source,
2089 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002090 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2091 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002092 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2093 f_sleep(1.0);
2094 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2095 f_sleep(1.0);
2096
2097 /* Terminate call */
2098 f_flow_delete(RTPEM[0]);
2099 f_flow_delete(RTPEM[1], ep, call_id);
2100
2101 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2102 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2103 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2104 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002105 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002106 }
2107 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2108 if (stats[2].num_pkts_rx_err_disabled != 0) {
2109 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002110 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002111 }
2112
Philipp Maier36291392018-07-25 09:40:44 +02002113 f_rtpem_stats_err_check(stats[0]);
2114 f_rtpem_stats_err_check(stats[1]);
2115 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002116
Philipp Maier2321ef92018-06-27 17:52:04 +02002117 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002118 }
Harald Weltef53f1642017-11-18 19:57:11 +01002119
Philipp Maier6d4e0942019-02-21 17:35:01 +01002120
2121 /* create two local RTP emulations; create two connections on MGW EP, see if
2122 * exchanged data is converted bwtween ts101318 and rfc5993 */
2123 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2124 var RtpFlowData flow[2];
2125 var RtpemStats stats[2];
2126 var MgcpResponse resp;
2127 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2128 var MgcpCallId call_id := '1226'H;
2129
2130 f_init(ep);
2131
2132 /* Turn on conversion mode */
2133 f_vty_enter_config(MGWVTY);
2134 f_vty_transceive(MGWVTY, "mgcp");
2135 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2136
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002137 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002138 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002139 /* bind local RTP emulation sockets */
2140 flow[0].em.portnr := 10000;
2141 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2142 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2143 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2144 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2145 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2146
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002147 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002148 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002149 flow[1].em.portnr := 20000;
2150 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2151 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2152 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2153 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2154 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2155
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002156 /* Send RTP packets to connection #0, receive on connection #1 */
2157 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2158 f_sleep(0.5);
2159 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002160 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002161 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2162 f_sleep(0.5);
2163 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002164
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002165 /* Send RTP packets to connection #1, receive on connection #0 */
2166 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2167 f_sleep(0.5);
2168 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2169 f_sleep(1.0);
2170 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2171 f_sleep(0.5);
2172 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2173
2174 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002175 f_flow_delete(RTPEM[0]);
2176 f_flow_delete(RTPEM[1], ep, call_id);
2177
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002178 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002179 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2180 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002181 f_rtpem_stats_err_check(stats[0]);
2182 f_rtpem_stats_err_check(stats[1]);
2183
2184 /* Turn off conversion mode */
2185 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2186
2187 setverdict(pass);
2188 }
2189
Philipp Maier4f764ce2019-03-07 10:54:10 +01002190 /* create two local RTP emulations; create two connections on MGW EP, see if
2191 * exchanged data is converted between AMR octet-aligned and bandwith
2192 * efficient-mode */
2193 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2194 var RtpFlowData flow[2];
2195 var RtpemStats stats[2];
2196 var MgcpResponse resp;
2197 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2198 var MgcpCallId call_id := '1226'H;
2199
2200 f_init(ep);
2201
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002202 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002203 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002204 /* bind local RTP emulation sockets */
2205 flow[0].em.portnr := 10000;
2206 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2207 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2208 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2209 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2210 flow[0].fmtp := fmtp0;
2211 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2212
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002213 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002214 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002215 flow[1].em.portnr := 20000;
2216 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2217 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2218 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2219 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2220 flow[1].fmtp := fmtp1;
2221 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2222
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002223 /* Send RTP packets to connection #0, receive on connection #1 */
2224 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2225 f_sleep(0.5);
2226 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002227 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002228 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2229 f_sleep(0.5);
2230 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002231
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002232 /* Send RTP packets to connection #1, receive on connection #0 */
2233 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2234 f_sleep(0.5);
2235 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2236 f_sleep(1.0);
2237 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2238 f_sleep(0.5);
2239 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2240
2241 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002242 f_flow_delete(RTPEM[0]);
2243 f_flow_delete(RTPEM[1], ep, call_id);
2244
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002245 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002246 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2247 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002248 f_rtpem_stats_err_check(stats[0]);
2249 f_rtpem_stats_err_check(stats[1]);
2250
2251 setverdict(pass);
2252 }
2253
Philipp Maier882843d2020-05-25 15:33:13 +02002254 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2255 * functions are real world AMR RTP payloads including AMR header. The
2256 * payloads were extracted from a trace with known good payloads. */
2257
Philipp Maier4f764ce2019-03-07 10:54:10 +01002258 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002259 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002260 }
2261
2262 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2263 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2264 }
2265
2266 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2267 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2268 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002269
Harald Weltee636afd2017-09-17 16:24:09 +08002270 /* TODO: Double-DLCX (no retransmission) */
2271
2272
2273
2274 /* TODO: AUEP (various) */
2275 /* TODO: RSIP (various) */
2276 /* TODO: RQNT (various) */
2277 /* TODO: EPCF (various) */
2278 /* TODO: AUCX (various) */
2279 /* TODO: invalid verb (various) */
2280
Oliver Smith021141e2019-06-25 12:09:01 +02002281
2282 testcase TC_conn_timeout() runs on dummy_CT {
2283 var RtpFlowData flow;
2284 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2285 var MgcpCallId call_id := '1225'H;
2286 var MGCP_RecvFrom mrf;
2287
2288 f_init(ep);
2289 log("Setting conn-timeout to 1s");
2290 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2291
2292 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002293 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002294 flow.em.portnr := 10000;
2295 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2296 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2297 f_sleep(1.5);
2298
2299 log("Stopping for 0.5s and resuming");
2300 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2301 f_sleep(0.5);
2302 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2303 f_sleep(0.1);
2304
2305 log("Stopping for 1.5s, expecting to run into timeout");
2306 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2307 f_sleep(1.5);
2308
2309 log("Resuming should fail now");
2310 f_rtpem_conn_refuse_expect(RTPEM[0]);
2311 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2312 f_sleep(0.2);
2313 f_rtpem_conn_refuse_verify(RTPEM[0]);
2314
2315 setverdict(pass);
2316 }
2317
Philipp Maier2609c752020-07-08 12:38:09 +02002318 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2319 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2320 var template MgcpCommand cmd;
2321 var MgcpResponse resp;
2322 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2323 var MgcpCallId call_id := '8376F297'H;
2324
2325 f_init(ep);
2326
2327 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2328 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2329
2330 f_dlcx_ok(ep);
2331
2332 setverdict(pass);
2333 }
2334
2335 /* Test what happens when overlapping endpoints are selected (E1) */
2336 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2337 var template MgcpCommand cmd;
2338 var MgcpResponse resp;
2339 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2340 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2341 var MgcpCallId call_id_1 := '8376F297'H;
2342 var MgcpCallId call_id_2 := '837AF2A7'H;
2343
2344 f_init();
2345
2346 /* ep_1 and ep_2 are overlapping, selecting both one after
2347 * another should work fine: */
2348 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2349 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2350 f_dlcx_ok(ep_1);
2351 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2352 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2353 f_dlcx_ok(ep_2);
2354
2355 /* When ep_1 is serving a call we can not select ep_2 becaus
2356 * it is overlapping with ep_1 */
2357 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2358 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2359 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2360 resp := mgcp_transceive_mgw(cmd, ?);
2361 if (resp.line.code != "501") {
2362 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2363 }
2364 f_dlcx_ok(ep_1);
2365
2366 setverdict(pass);
2367 }
2368
2369 /* Create one connection in loopback mode, test if the RTP packets are
2370 * actually reflected */
2371 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2372 var RtpFlowData flow;
2373 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2374 var MgcpCallId call_id := '12250989'H;
2375 var RtpemStats stats;
2376
2377 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002378 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002379 flow.em.portnr := 10000;
2380 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2381
2382 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2383 f_sleep(1.0);
2384 f_flow_delete(RTPEM[0], ep, call_id);
2385
2386 stats := f_rtpem_stats_get(RTPEM[0]);
2387
2388 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2389 setverdict(fail);
2390 }
2391 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2392 setverdict(fail);
2393 }
2394
2395 f_rtpem_stats_err_check(stats);
2396
2397 setverdict(pass);
2398 }
2399
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002400 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2401 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2402 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2403 var template MgcpCommand cmd;
2404 var MgcpResponse resp;
2405 var MgcpCallId call_id := '1234'H;
2406 var MgcpConnectionId conn_id;
2407
2408 f_init(ep);
2409
2410 /* create the connection on the MGW */
2411 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2412 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2413 conn_id := extract_conn_id(resp);
2414
2415 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2416 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2417 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2418 valueof(ts_SDP_ptime(20)) });
2419 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2420
2421 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2422 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2423 }
2424 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2425 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2426 }
2427
2428 /* clean-up */
2429 f_dlcx_ok(ep, call_id);
2430 setverdict(pass);
2431 }
2432
2433 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2434 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2435 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2436 var template MgcpCommand cmd;
2437 var MgcpResponse resp;
2438 var MgcpCallId call_id := '1234'H;
2439 var MgcpConnectionId conn_id;
2440
2441 f_init(ep);
2442
2443 /* create the connection on the MGW */
2444 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2445 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2446 conn_id := extract_conn_id(resp);
2447
2448 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2449 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2450 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2451 valueof(ts_SDP_ptime(20)) });
2452 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2453
2454 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2455 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2456 }
2457 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2458 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2459 }
2460
2461 /* clean-up */
2462 f_dlcx_ok(ep, call_id);
2463 setverdict(pass);
2464 }
2465
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002466 /* create two local RTP emulations and pass data in both directions */
2467 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
2468 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2469 var RtpFlowData flow[2];
2470 var RtpemStats stats[2];
2471 var MgcpResponse resp;
2472 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2473 var MgcpCallId call_id := '1227'H;
2474 var integer num_pkts_tx[2];
2475 var integer temp;
2476
2477 f_init(ep);
2478
2479 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2480 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2481 flow[0].em.portnr := 10000;
2482 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2483 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2484 flow[0].rtp_cfg.iuup_mode := true;
2485 flow[0].rtp_cfg.iuup_tx_init := true;
2486 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2487 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2488
2489 /* Create the second connection. This connection will be also
2490 * in receive only mode (CN side, IuUP-Init passive) */
2491 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2492 flow[1].em.portnr := 20000;
2493 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2494 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2495 flow[1].rtp_cfg.iuup_mode := true;
2496 flow[1].rtp_cfg.iuup_tx_init := false;
2497 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2498 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2499
2500 /* The first leg starts transmitting */
2501 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2502 f_sleep(0.5);
2503 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2504 if (stats[0].num_pkts_rx_err_disabled != 0) {
2505 setverdict(fail, "received packets from MGW on recvonly connection 0");
2506 mtc.stop;
2507 }
2508 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2509 if (stats[1].num_pkts_rx_err_disabled != 0) {
2510 setverdict(fail, "received packets from MGW on recvonly connection 1");
2511 mtc.stop;
2512 }
2513
2514 /* The second leg starts transmitting a little later */
2515 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2516 f_sleep(1.0);
2517 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2518 if (stats[0].num_pkts_rx_err_disabled != 0) {
2519 setverdict(fail, "received packets from MGW on recvonly connection 0");
2520 mtc.stop;
2521 }
2522 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2523 if (stats[1].num_pkts_rx_err_disabled != 0) {
2524 setverdict(fail, "received packets from MGW on recvonly connection 1");
2525 mtc.stop;
2526 }
2527
2528 /* The first leg will now be switched into bidirectional
2529 * mode, but we do not expect any data coming back yet. */
2530 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2531 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2532 num_pkts_tx[1] := stats[1].num_pkts_tx;
2533 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2534 f_sleep(0.5);
2535 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2536 if (stats[0].num_pkts_rx_err_disabled != 0) {
2537 setverdict(fail, "received packets from MGW on recvonly connection 0");
2538 mtc.stop;
2539 }
2540 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2541 if (stats[1].num_pkts_rx_err_disabled != 0) {
2542 setverdict(fail, "received packets from MGW on recvonly connection 1");
2543 mtc.stop;
2544 }
2545
2546 /* When the second leg is switched into bidirectional mode
2547 * as well, then the MGW will connect the two together and
2548 * we should see RTP streams passing through from both ends. */
2549 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2550 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2551 num_pkts_tx[0] := stats[0].num_pkts_tx;
2552 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2553 f_sleep(2.0);
2554
2555 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2556 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2557
2558 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2559 if (temp > 3 or temp < -3) {
2560 setverdict(fail, "number of packets not within normal parameters:", temp);
2561 mtc.stop;
2562 }
2563
2564 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2565 if (temp > 3 or temp < -3) {
2566 setverdict(fail, "number of packets not within normal parameters:", temp);
2567 mtc.stop;
2568 }
2569
2570 f_rtpem_stats_err_check(stats[0]);
2571 f_rtpem_stats_err_check(stats[1]);
2572
2573 /* Tear down */
2574 f_flow_delete(RTPEM[0]);
2575 f_flow_delete(RTPEM[1], ep, call_id);
2576 setverdict(pass);
2577 }
2578 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
2579 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4,
2580 mp_local_ipv4, mp_remote_ipv4);
2581 }
2582
Harald Welte00a067f2017-09-13 23:27:17 +02002583 control {
2584 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002585 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002586 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002587 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002588 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002589 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002590 execute(TC_crcx_early_bidir_mode());
2591 execute(TC_crcx_unsupp_param());
2592 execute(TC_crcx_missing_callid());
2593 execute(TC_crcx_missing_mode());
2594 execute(TC_crcx_unsupp_packet_intv());
2595 execute(TC_crcx_illegal_double_lco());
2596 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002597 execute(TC_crcx_wildcarded());
2598 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002599 execute(TC_mdcx_without_crcx());
2600 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02002601 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002602 execute(TC_mdcx_wildcarded());
2603 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002604 execute(TC_crcx_and_dlcx_ep_callid_connid());
2605 execute(TC_crcx_and_dlcx_ep_callid());
2606 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002607 execute(TC_crcx_and_dlcx_ep_callid_inval());
2608 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002609 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002610
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002611 execute(TC_crcx_osmux_wildcard());
2612 execute(TC_crcx_osmux_fixed());
2613 execute(TC_crcx_osmux_fixed_twice());
2614 execute(TC_one_crcx_receive_only_osmux());
2615 execute(TC_one_crcx_loopback_osmux());
2616 execute(TC_two_crcx_and_rtp_osmux());
2617 execute(TC_two_crcx_and_rtp_osmux_bidir());
2618 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2619 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2620
Harald Welte33d82162017-12-28 03:21:57 +01002621 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002622
2623 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002624
2625 execute(TC_one_crcx_receive_only_rtp());
2626 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002627 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02002628 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002629 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002630 execute(TC_two_crcx_diff_pt_and_rtp());
2631 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002632 execute(TC_two_crcx_mdcx_and_rtp());
2633 execute(TC_two_crcx_and_unsolicited_rtp());
2634 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002635 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002636 execute(TC_amr_oa_bwe_rtp_conversion());
2637 execute(TC_amr_oa_oa_rtp_conversion());
2638 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002639
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002640 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002641
2642 execute(TC_e1_crcx_and_dlcx_ep());
2643 execute(TC_e1_crcx_with_overlap());
2644 execute(TC_e1_crcx_loopback());
2645
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002646 execute(TC_crcx_mdcx_ip4());
2647 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002648 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2649 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2650 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2651 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2652 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2653 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2654 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2655 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02002656
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002657 execute(TC_two_crcx_mdcx_and_iuup());
2658
Philipp Maier37965082021-05-25 16:44:25 +02002659 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
2660 * older versions of osmo-mgw. This eventually leads into
2661 * a failure of all subsequent testcases, so it is important
2662 * not to add new testcaes after this one. */
2663 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02002664 }
2665}