blob: b5b91f0c2a288b94948c90d12bff5f24e5819c00 [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
1100 expect := {
1101 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := n_endpoints, max := n_endpoints}
1102 };
1103 f_statsd_expect(expect);
1104
1105 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001106 var template MgcpResponse rtmpl := {
1107 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001108 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001109 string := ?
1110 },
1111 params:= { },
1112 sdp := omit
1113 };
Philipp Maier55b90542021-07-02 12:33:19 +02001114 cmd := ts_DLCX(get_next_trans_id(), ep);
1115 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001116
Philipp Maier55b90542021-07-02 12:33:19 +02001117 /* The stats reporter collects multiple samples during the reporting interval and
1118 * reports the highest back to the user. This means we will not immediately get
1119 * the 0 endpoints but an intermediate result instead. */
1120 expect := {
1121 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := n_endpoints}
1122 };
1123 f_statsd_expect(expect);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001124
Philipp Maier55b90542021-07-02 12:33:19 +02001125 /* The second interval must resturn a result with 0 endpoints in use. */
1126 expect := {
1127 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1128 };
1129 f_statsd_expect(expect);
1130
Philipp Maier8a3dc922018-02-02 14:55:12 +01001131 setverdict(pass);
1132 }
1133
Harald Welte79181ff2017-11-18 19:26:11 +01001134 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1135 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1136 var template MgcpCommand cmd;
1137 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001138 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001139 var MgcpCallId call_id := '51234'H;
1140
1141 f_init(ep);
1142
1143 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1144 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1145
1146 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1147
1148 setverdict(pass);
1149 }
1150
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001151 /* test valid CRCX without SDP */
1152 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1153 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1154 setverdict(pass);
1155 }
1156
1157 /* test valid CRCX without SDP */
1158 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1159 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1160 setverdict(pass);
1161 }
1162
1163 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1164 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1165 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1166 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1167 setverdict(pass);
1168 }
1169
1170 /* Create one half open connection in receive-only mode. The MGW must accept
1171 * the packets but must not send any. */
1172 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1173 var RtpFlowData flow;
1174 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1175 var MgcpCallId call_id := '1225'H;
1176 var OsmuxemStats stats;
1177 var OsmuxTxHandle tx_hdl;
1178
1179 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001180 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001181 flow.em.portnr := mp_local_osmux_port;
1182 flow.osmux_cid := -1;
1183 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1184
1185 /* create a transmitter not yet known by MGW */
1186 tx_hdl := valueof(t_TxHandleAMR590(2));
1187 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1188
1189 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1190 f_sleep(1.0);
1191 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1192
1193 stats := f_osmuxem_stats_get(OsmuxEM);
1194
1195 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1196 setverdict(fail);
1197 }
1198 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1199 setverdict(fail);
1200 }
1201
1202 f_osmuxem_stats_err_check(stats);
1203
1204 setverdict(pass);
1205 }
1206
1207 /* Create one connection in loopback mode, test if the Osmux packets are
1208 * actually reflected */
1209 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1210 var RtpFlowData flow;
1211 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1212 var MgcpCallId call_id := '1225'H;
1213 var OsmuxemStats stats;
1214 var OsmuxTxHandle tx_hdl;
1215
1216 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001217 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001218 flow.em.portnr := mp_local_osmux_port;
1219 flow.osmux_cid := 2;
1220 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1221
1222 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1223 f_sleep(1.0);
1224
1225 /* Switch off both Tx, wait to receive delayed frames from MGW */
1226 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1227 f_sleep(0.1);
1228 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1229
1230 stats := f_osmuxem_stats_get(OsmuxEM);
1231
1232 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1233 setverdict(fail);
1234 }
1235 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1236 setverdict(fail);
1237 }
1238
1239 f_osmuxem_stats_err_check(stats);
1240
1241 setverdict(pass);
1242 }
1243
1244 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1245 * must match the reception statistics on the other side and vice versa. The
1246 * user may also supply a tolerance value (number of packets) when deviations
1247 * are acceptable */
1248 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1249 var integer plen;
1250
1251 log("stats A: ", a);
1252 log("stats B: ", b);
1253 log("tolerance: ", tolerance, " packets");
1254 log("batch_size: ", batch_size, " packets");
1255
1256 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1257
1258 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1259 return false;
1260 }
1261
1262 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1263 return false;
1264 }
1265
1266 if(a.num_pkts_tx > 0) {
1267 plen := a.bytes_payload_tx / a.num_pkts_tx;
1268 } else {
1269 plen := 0;
1270 }
1271
1272 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1273 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1274 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1275 return false;
1276 }
1277
1278 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) {
1279 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1280 return false;
1281 }
1282
1283 return true;
1284 }
1285
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001286 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1287 charstring local_ip_rtp, charstring remote_ip_rtp,
1288 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001289 var RtpFlowData flow[2];
1290 var RtpemStats stats_rtp;
1291 var OsmuxemStats stats_osmux;
1292 var MgcpResponse resp;
1293 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1294 var MgcpCallId call_id := '1226'H;
1295 var integer tolerance := 0;
1296
1297 f_init(ep, true);
1298
1299 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001300 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001301 flow[0].rtp_cfg := c_RtpemDefaultCfg
1302 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1303 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1304 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);
1305 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1306 /* bind local RTP emulation sockets */
1307 flow[0].em.portnr := 10000;
1308 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1309
1310 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001311 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001312 flow[1].em.portnr := mp_local_osmux_port;
1313 flow[1].osmux_cid := 2;
1314 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1315 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1316
1317 if (bidir) {
1318 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1319 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1320
1321 /* Note: When we test bidirectional we may
1322 * loose packets during switch off because
1323 * both ends are transmitting and we only
1324 * can switch them off one by one. */
1325 tolerance := 3;
1326 } else {
1327 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1328 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1329 }
1330
1331 f_sleep(1.0);
1332
1333 /* Switch off both Tx, wait to receive delayed frames from MGW */
1334 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1335 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1336 f_sleep(0.1);
1337
1338 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1339 f_flow_delete(RTPEM[1]);
1340
1341 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1342 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1343 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1344 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1345 mtc.stop;
1346 }
1347
1348 f_rtpem_stats_err_check(stats_rtp);
1349 f_osmuxem_stats_err_check(stats_osmux);
1350
1351 setverdict(pass);
1352 }
1353
1354 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1355 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001356 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1357 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001358 }
1359
1360 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1361 * exchange some data in both directions */
1362 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001363 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1364 mp_local_ipv4, mp_remote_ipv4);
1365 }
1366
1367 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1368 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1369 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1370 mp_local_ipv6, mp_remote_ipv6);
1371 }
1372 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1373 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1374 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1375 mp_local_ipv6, mp_remote_ipv6);
1376 }
1377 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1378 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1379 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1380 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001381 }
1382
1383
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001384 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1385 charstring local_ip_rtp, charstring remote_ip_rtp,
1386 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001387 var RtpFlowData flow[2];
1388 var RtpemStats stats_rtp;
1389 var OsmuxemStats stats_osmux;
1390 var MgcpResponse resp;
1391 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1392 var MgcpCallId call_id := '1227'H;
1393 var integer num_pkts_tx[2];
1394 var integer temp;
1395
1396 f_init(ep, true);
1397
1398 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001399 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001400 flow[0].rtp_cfg := c_RtpemDefaultCfg
1401 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1402 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1403 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);
1404 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1405 /* bind local RTP emulation sockets */
1406 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001407 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001408
1409
1410 /* Create the second connection. This connection will be also
1411 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001412 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001413 flow[1].em.portnr := mp_local_osmux_port;
1414 if (crcx_osmux_wildcard) {
1415 flow[1].osmux_cid := -1;
1416 } else {
1417 flow[1].osmux_cid := 2;
1418 }
1419 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001420 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001421
1422
1423 /* The first leg starts transmitting */
1424 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1425 f_sleep(0.5);
1426 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1427 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1428 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1429 mtc.stop;
1430 }
1431 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1432 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1433 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1434 mtc.stop;
1435 }
1436
1437 /* The second leg starts transmitting a little later */
1438 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1439 f_sleep(1.0);
1440 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1441 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1442 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1443 mtc.stop;
1444 }
1445 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1446 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1447 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1448 mtc.stop;
1449 }
1450
1451 /* The first leg will now be switched into bidirectional
1452 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001453 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1454 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1455 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001456 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1457 f_sleep(0.5);
1458 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1459 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1460 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1461 mtc.stop;
1462 }
1463 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1464 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1465 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1466 mtc.stop;
1467 }
1468
1469 /* When the second leg is switched into bidirectional mode
1470 * as well, then the MGW will connect the two together and
1471 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001472 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1473 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001474 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001475
1476 if (crcx_osmux_wildcard) {
1477 /* For now we must set same CID as the MGW recvCID,
1478 * having sendCID!=recvCID is not yet supported. */
1479 flow[1].osmux_cid := flow[1].osmux_cid_response;
1480 }
1481 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1482 f_sleep(2.0);
1483
1484 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1485 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1486
1487 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1488 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1489 log("stats_rtp: ", stats_rtp);
1490 log("stats_osmux: ", stats_osmux);
1491 log("old_rtp_tx: ", num_pkts_tx[0]);
1492 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1493 mtc.stop;
1494 }
1495
1496 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1497 if (temp > 3 or temp < -3) {
1498 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1499 mtc.stop;
1500 }
1501
1502 f_rtpem_stats_err_check(stats_rtp);
1503 f_osmuxem_stats_err_check(stats_osmux);
1504
1505 /* Tear down */
1506 f_flow_delete(RTPEM[0]);
1507 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1508 setverdict(pass);
1509 }
1510
1511 /* create one RTP and one OSmux emulations and pass data in both
1512 directions. Create CRCX with wildcard Osmux CID and set it later
1513 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1514 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001515 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1516 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001517 }
1518
1519 /* create one RTP and one OSmux emulations and pass data in both
1520 directions. Create CRCX with fixed Osmux CID and keep it during
1521 MDCX. This is similar to how BSC sets up the call in AoIP. */
1522 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001523 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1524 mp_local_ipv4, mp_remote_ipv4);
1525 }
1526
1527 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1528 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1529 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1530 mp_local_ipv6, mp_remote_ipv6);
1531 }
1532 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1533 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1534 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1535 mp_local_ipv6, mp_remote_ipv6);
1536 }
1537 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1538 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1539 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1540 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001541 }
1542
Harald Welte646ecdb2017-12-28 03:21:57 +01001543 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1544 var template MgcpCommand cmd;
1545 var MgcpResponse resp;
1546
1547 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1548 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1549
1550 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1551
1552 setverdict(pass);
1553 }
1554
1555 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1556 var MgcpEndpoint ep;
1557 var MgcpCallId call_id;
1558 var integer ep_nr;
1559
1560 f_init();
1561
1562 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001563 if(ep_nr > 15) {
1564 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1565 } else {
1566 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1567 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001568 call_id := int2hex(ep_nr, 2) & '1234'H;
1569 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1570 }
1571 }
1572
Harald Welte79181ff2017-11-18 19:26:11 +01001573 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1574 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001575 var template MgcpCommand cmd;
1576 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001577 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001578 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001579
Harald Welteedc45c12017-11-18 19:15:05 +01001580 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001581
Harald Welteba62c8c2017-11-18 18:26:49 +01001582 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001583 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001584
Harald Welteba62c8c2017-11-18 18:26:49 +01001585 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001586
1587 setverdict(pass);
1588 }
1589
Harald Welte79181ff2017-11-18 19:26:11 +01001590 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1591 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1592 var template MgcpCommand cmd;
1593 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001594 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001595 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001596
1597 f_init(ep);
1598
1599 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1600 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1601
1602 f_dlcx_ok(ep);
1603
1604 setverdict(pass);
1605 }
1606
1607
Harald Welte6d167f82017-11-18 19:41:35 +01001608 /* CRCX + DLCX of valid endpoint but invalid call-id */
1609 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1610 var template MgcpCommand cmd;
1611 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001612 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001613 var MgcpCallId call_id := '51231'H;
1614
1615 f_init(ep);
1616
1617 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1618 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1619
1620 f_dlcx(ep, "516", *, 'ffff'H);
1621
1622 setverdict(pass);
1623 }
1624
1625
1626 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1627 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1628 var template MgcpCommand cmd;
1629 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001630 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001631 var MgcpCallId call_id := '51230'H;
1632
1633 f_init(ep);
1634
1635 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1636 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1637
1638 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1639
1640 setverdict(pass);
1641 }
1642
1643
Harald Weltee636afd2017-09-17 16:24:09 +08001644 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001645 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1646 var template MgcpCommand cmd;
1647 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001648 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001649 var MgcpCallId call_id := '51229'H;
1650 var template MgcpResponse rtmpl := {
1651 line := {
1652 code := "200",
1653 string := "OK"
1654 },
1655 params:= { },
1656 sdp := omit
1657 };
1658
1659 f_init(ep);
1660
1661 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1662 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1663
1664 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1665 resp := mgcp_transceive_mgw(cmd, rtmpl);
1666 resp := mgcp_transceive_mgw(cmd, rtmpl);
1667
1668 setverdict(pass);
1669 }
1670
Harald Weltebb7523b2018-03-29 08:52:01 +02001671 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1672 charstring codec) := {
1673 em := {
1674 hostname := host_a,
1675 portnr := omit
1676 },
1677 mgw := {
1678 hostname := host_b,
1679 portnr := omit
1680 },
1681 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001682 codec := codec,
1683 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001684 }
Harald Weltef53f1642017-11-18 19:57:11 +01001685
Harald Weltebb7523b2018-03-29 08:52:01 +02001686 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1687 testcase TC_rtpem_selftest() runs on dummy_CT {
1688 var RtpemStats stats[2];
1689 var integer local_port := 10000;
1690 var integer local_port2 := 20000;
1691
1692 f_init();
1693
1694 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1695 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1696
1697 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1698 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1699
1700 log("=== starting");
1701 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1702 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1703
1704 f_sleep(5.0);
1705
1706 log("=== stopping");
1707 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1708 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1709 f_sleep(0.5);
1710 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1711 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1712
1713 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1714 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1715 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1716 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001717 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001718 }
1719 setverdict(pass);
1720 }
1721
Philipp Maier2321ef92018-06-27 17:52:04 +02001722 /* Create one half open connection in receive-only mode. The MGW must accept
1723 * the packets but must not send any. */
1724 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1725 var RtpFlowData flow;
1726 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1727 var MgcpCallId call_id := '1225'H;
1728 var RtpemStats stats;
1729
1730 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001731 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001732 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001733 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001734
1735 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1736 f_sleep(1.0);
1737 f_flow_delete(RTPEM[0], ep, call_id);
1738
1739 stats := f_rtpem_stats_get(RTPEM[0]);
1740
Philipp Maier42b17cc2019-10-01 13:53:17 +02001741 /* Make sure that at least some amount of RTP packets/bytes
1742 * have has been transmitted. The compare values for
1743 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1744 * using a testrun and the results were devided by 2, so even
1745 * in load situations we should reach the minimum amount of
1746 * required packets/bytes */
1747
1748 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001749 setverdict(fail);
1750 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001751 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001752 setverdict(fail);
1753 }
Philipp Maier36291392018-07-25 09:40:44 +02001754
1755 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001756
1757 setverdict(pass);
1758 }
1759
1760 /* Create one connection in loopback mode, test if the RTP packets are
1761 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001762 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 +02001763 var RtpFlowData flow;
1764 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1765 var MgcpCallId call_id := '1225'H;
1766 var RtpemStats stats;
1767
1768 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001769 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001770 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001771 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001772
1773 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1774 f_sleep(1.0);
1775 f_flow_delete(RTPEM[0], ep, call_id);
1776
1777 stats := f_rtpem_stats_get(RTPEM[0]);
1778
1779 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1780 setverdict(fail);
1781 }
1782 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1783 setverdict(fail);
1784 }
Philipp Maier36291392018-07-25 09:40:44 +02001785
1786 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001787
1788 setverdict(pass);
1789 }
1790
Philipp Maier1ac13982021-05-07 23:06:07 +02001791 /* Create one connection in loopback mode, test if the RTP packets are
1792 * actually reflected */
1793 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001794 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001795 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001796 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1797 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1798 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001799
1800 /* Same as above, but we will intenionally not tell the MGW where to
1801 * send the outgoing traffic. The connection is still created in
1802 * loopback mode, so the MGW should take the originating address from
1803 * the incoming RTP packet and send it back to the source */
1804 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001805 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001806 }
1807
1808
Philipp Maier7df85f62018-07-25 10:26:09 +02001809 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1810 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001811 var RtpFlowData flow[2];
1812 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001813 var MgcpResponse resp;
1814 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1815 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001816 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001817
1818 f_init(ep);
1819
1820 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001821 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001822 /* bind local RTP emulation sockets */
1823 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001824 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001825
1826 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001827 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001828 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001829 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1830
1831 if (bidir) {
1832 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1833 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1834
1835 /* Note: When we test bidirectional we may
1836 * loose packets during switch off because
1837 * both ends are transmitting and we only
1838 * can switch them off one by one. */
1839 tolerance := 3;
1840 } else {
1841 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1842 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1843 }
1844
1845 f_sleep(1.0);
1846
1847 f_flow_delete(RTPEM[1]);
1848 f_flow_delete(RTPEM[0], ep, call_id);
1849
1850 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1851 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1852 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1853 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001854 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001855 }
1856
Philipp Maier36291392018-07-25 09:40:44 +02001857 f_rtpem_stats_err_check(stats[0]);
1858 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001859
Philipp Maier2321ef92018-06-27 17:52:04 +02001860 setverdict(pass);
1861 }
1862
1863 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1864 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001865 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001866 }
1867
1868 /* create two local RTP emulations; create two connections on MGW EP,
1869 * exchange some data in both directions */
1870 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001871 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1872 }
1873
1874 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1875 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1876 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1877 }
1878
1879 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1880 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1881 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001882 }
1883
1884 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001885 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1886 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001887 var RtpFlowData flow[2];
1888 var RtpemStats stats[2];
1889 var MgcpResponse resp;
1890 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1891 var MgcpCallId call_id := '1227'H;
1892 var integer num_pkts_tx[2];
1893 var integer temp;
1894
1895 f_init(ep);
1896
1897 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001898 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001899 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001900 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001901
1902 /* Create the second connection. This connection will be also
1903 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001904 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001905 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001906 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001907
1908 /* The first leg starts transmitting */
1909 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1910 f_sleep(0.5);
1911 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1912 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001913 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001914 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001915 }
1916 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1917 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001918 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001919 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001920 }
1921
1922 /* The second leg starts transmitting a little later */
1923 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1924 f_sleep(1.0);
1925 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1926 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001927 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001928 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001929 }
1930 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1931 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001932 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001933 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001934 }
1935
1936 /* The first leg will now be switched into bidirectional
1937 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001938 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1939 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1940 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001941 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1942 f_sleep(0.5);
1943 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001944 if (stats[0].num_pkts_rx_err_disabled != 0) {
1945 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001946 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001947 }
1948 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1949 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001950 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001951 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001952 }
1953
1954 /* When the second leg is switched into bidirectional mode
1955 * as well, then the MGW will connect the two together and
1956 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02001957 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1958 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001959 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001960 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1961 f_sleep(2.0);
1962
1963 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1964 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1965
1966 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1967 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001968 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001969 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001970 }
1971
1972 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1973 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001974 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001975 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001976 }
Philipp Maier36291392018-07-25 09:40:44 +02001977
1978 f_rtpem_stats_err_check(stats[0]);
1979 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001980
1981 /* Tear down */
1982 f_flow_delete(RTPEM[0]);
1983 f_flow_delete(RTPEM[1], ep, call_id);
1984 setverdict(pass);
1985 }
1986
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001987 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1988 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1989 mp_local_ipv4, mp_remote_ipv4);
1990 }
1991
1992 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
1993 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
1994 mp_local_ipv6, mp_remote_ipv6);
1995 }
1996
1997 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
1998 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1999 mp_local_ipv6, mp_remote_ipv6);
2000 }
2001
Philipp Maier2321ef92018-06-27 17:52:04 +02002002 /* Test what happens when two RTP streams from different sources target
2003 * a single connection. Is the unsolicited stream properly ignored? */
2004 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2005 var RtpFlowData flow[2];
2006 var RtpemStats stats[2];
2007 var MgcpResponse resp;
2008 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2009 var MgcpCallId call_id := '1234321326'H;
2010 var integer unsolicited_port := 10002;
2011
2012 f_init(ep);
2013
2014 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002015 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002016 /* bind local RTP emulation sockets */
2017 flow[0].em.portnr := 10000;
2018 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2019
2020 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002021 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002022 flow[1].em.portnr := 20000;
2023 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002024
2025 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2026 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2027
Philipp Maier2321ef92018-06-27 17:52:04 +02002028 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002029
Philipp Maier2321ef92018-06-27 17:52:04 +02002030 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002031 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2032 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002033 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2034
2035 f_sleep(0.5);
2036
Daniel Willmanna069d382018-12-13 13:53:33 +01002037 /* Stop transmitting packets and tear down the flows */
2038 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002039 f_flow_delete(RTPEM[0]);
2040 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002041
2042 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2043 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2044 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2045 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002046 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002047 }
2048
Philipp Maier36291392018-07-25 09:40:44 +02002049 f_rtpem_stats_err_check(stats[0]);
2050 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002051
Harald Weltebb7523b2018-03-29 08:52:01 +02002052 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002053 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002054
Philipp Maier2321ef92018-06-27 17:52:04 +02002055 /* Test a handover situation. We first create two connections transmit
2056 * some data bidirectionally. Then we will simulate a handover situation. */
2057 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2058 var RtpFlowData flow[2];
2059 var RtpemStats stats[3];
2060 var MgcpResponse resp;
2061 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2062 var MgcpCallId call_id := '76338'H;
2063 var integer port_old;
2064
2065 f_init(ep);
2066
2067 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002068 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002069 /* bind local RTP emulation sockets */
2070 flow[0].em.portnr := 10000;
2071 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2072
2073 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002074 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002075 flow[1].em.portnr := 20000;
2076 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2077
2078 /* Normal rtp flow for one second */
2079 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2080 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2081 f_sleep(1.0);
2082
2083 /* Now switch the flow over to a new port (BTS) */
2084 port_old := flow[0].em.portnr;
2085 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002086 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002087 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002088 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002089
2090 /* When handing over a call, the old source may still keep
2091 * transmitting for a while. We simulate this by injecting
2092 * some unsolicited packets on the behalf of the old source,
2093 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002094 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2095 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002096 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2097 f_sleep(1.0);
2098 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2099 f_sleep(1.0);
2100
2101 /* Terminate call */
2102 f_flow_delete(RTPEM[0]);
2103 f_flow_delete(RTPEM[1], ep, call_id);
2104
2105 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2106 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2107 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2108 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002109 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002110 }
2111 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2112 if (stats[2].num_pkts_rx_err_disabled != 0) {
2113 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002114 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002115 }
2116
Philipp Maier36291392018-07-25 09:40:44 +02002117 f_rtpem_stats_err_check(stats[0]);
2118 f_rtpem_stats_err_check(stats[1]);
2119 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002120
Philipp Maier2321ef92018-06-27 17:52:04 +02002121 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002122 }
Harald Weltef53f1642017-11-18 19:57:11 +01002123
Philipp Maier6d4e0942019-02-21 17:35:01 +01002124
2125 /* create two local RTP emulations; create two connections on MGW EP, see if
2126 * exchanged data is converted bwtween ts101318 and rfc5993 */
2127 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2128 var RtpFlowData flow[2];
2129 var RtpemStats stats[2];
2130 var MgcpResponse resp;
2131 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2132 var MgcpCallId call_id := '1226'H;
2133
2134 f_init(ep);
2135
2136 /* Turn on conversion mode */
2137 f_vty_enter_config(MGWVTY);
2138 f_vty_transceive(MGWVTY, "mgcp");
2139 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2140
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002141 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002142 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002143 /* bind local RTP emulation sockets */
2144 flow[0].em.portnr := 10000;
2145 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2146 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2147 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2148 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2149 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2150
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002151 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002152 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002153 flow[1].em.portnr := 20000;
2154 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2155 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2156 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2157 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2158 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2159
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002160 /* Send RTP packets to connection #0, receive on connection #1 */
2161 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2162 f_sleep(0.5);
2163 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002164 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002165 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2166 f_sleep(0.5);
2167 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002168
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002169 /* Send RTP packets to connection #1, receive on connection #0 */
2170 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2171 f_sleep(0.5);
2172 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2173 f_sleep(1.0);
2174 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2175 f_sleep(0.5);
2176 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2177
2178 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002179 f_flow_delete(RTPEM[0]);
2180 f_flow_delete(RTPEM[1], ep, call_id);
2181
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002182 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002183 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2184 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002185 f_rtpem_stats_err_check(stats[0]);
2186 f_rtpem_stats_err_check(stats[1]);
2187
2188 /* Turn off conversion mode */
2189 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2190
2191 setverdict(pass);
2192 }
2193
Philipp Maier4f764ce2019-03-07 10:54:10 +01002194 /* create two local RTP emulations; create two connections on MGW EP, see if
2195 * exchanged data is converted between AMR octet-aligned and bandwith
2196 * efficient-mode */
2197 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2198 var RtpFlowData flow[2];
2199 var RtpemStats stats[2];
2200 var MgcpResponse resp;
2201 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2202 var MgcpCallId call_id := '1226'H;
2203
2204 f_init(ep);
2205
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002206 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002207 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002208 /* bind local RTP emulation sockets */
2209 flow[0].em.portnr := 10000;
2210 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2211 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2212 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2213 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2214 flow[0].fmtp := fmtp0;
2215 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2216
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002217 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002218 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002219 flow[1].em.portnr := 20000;
2220 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2221 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2222 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2223 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2224 flow[1].fmtp := fmtp1;
2225 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2226
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002227 /* Send RTP packets to connection #0, receive on connection #1 */
2228 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2229 f_sleep(0.5);
2230 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002231 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002232 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2233 f_sleep(0.5);
2234 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002235
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002236 /* Send RTP packets to connection #1, receive on connection #0 */
2237 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2238 f_sleep(0.5);
2239 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2240 f_sleep(1.0);
2241 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2242 f_sleep(0.5);
2243 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2244
2245 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002246 f_flow_delete(RTPEM[0]);
2247 f_flow_delete(RTPEM[1], ep, call_id);
2248
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002249 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002250 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2251 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002252 f_rtpem_stats_err_check(stats[0]);
2253 f_rtpem_stats_err_check(stats[1]);
2254
2255 setverdict(pass);
2256 }
2257
Philipp Maier882843d2020-05-25 15:33:13 +02002258 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2259 * functions are real world AMR RTP payloads including AMR header. The
2260 * payloads were extracted from a trace with known good payloads. */
2261
Philipp Maier4f764ce2019-03-07 10:54:10 +01002262 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002263 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002264 }
2265
2266 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2267 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2268 }
2269
2270 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2271 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2272 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002273
Harald Weltee636afd2017-09-17 16:24:09 +08002274 /* TODO: Double-DLCX (no retransmission) */
2275
2276
2277
2278 /* TODO: AUEP (various) */
2279 /* TODO: RSIP (various) */
2280 /* TODO: RQNT (various) */
2281 /* TODO: EPCF (various) */
2282 /* TODO: AUCX (various) */
2283 /* TODO: invalid verb (various) */
2284
Oliver Smith021141e2019-06-25 12:09:01 +02002285
2286 testcase TC_conn_timeout() runs on dummy_CT {
2287 var RtpFlowData flow;
2288 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2289 var MgcpCallId call_id := '1225'H;
2290 var MGCP_RecvFrom mrf;
2291
2292 f_init(ep);
2293 log("Setting conn-timeout to 1s");
2294 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2295
2296 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002297 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002298 flow.em.portnr := 10000;
2299 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2300 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2301 f_sleep(1.5);
2302
2303 log("Stopping for 0.5s and resuming");
2304 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2305 f_sleep(0.5);
2306 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2307 f_sleep(0.1);
2308
2309 log("Stopping for 1.5s, expecting to run into timeout");
2310 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2311 f_sleep(1.5);
2312
2313 log("Resuming should fail now");
2314 f_rtpem_conn_refuse_expect(RTPEM[0]);
2315 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2316 f_sleep(0.2);
2317 f_rtpem_conn_refuse_verify(RTPEM[0]);
2318
2319 setverdict(pass);
2320 }
2321
Philipp Maier2609c752020-07-08 12:38:09 +02002322 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2323 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2324 var template MgcpCommand cmd;
2325 var MgcpResponse resp;
2326 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2327 var MgcpCallId call_id := '8376F297'H;
2328
2329 f_init(ep);
2330
2331 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2332 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2333
2334 f_dlcx_ok(ep);
2335
2336 setverdict(pass);
2337 }
2338
2339 /* Test what happens when overlapping endpoints are selected (E1) */
2340 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2341 var template MgcpCommand cmd;
2342 var MgcpResponse resp;
2343 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2344 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2345 var MgcpCallId call_id_1 := '8376F297'H;
2346 var MgcpCallId call_id_2 := '837AF2A7'H;
2347
2348 f_init();
2349
2350 /* ep_1 and ep_2 are overlapping, selecting both one after
2351 * another should work fine: */
2352 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2353 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2354 f_dlcx_ok(ep_1);
2355 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2356 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2357 f_dlcx_ok(ep_2);
2358
2359 /* When ep_1 is serving a call we can not select ep_2 becaus
2360 * it is overlapping with ep_1 */
2361 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2362 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2363 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2364 resp := mgcp_transceive_mgw(cmd, ?);
2365 if (resp.line.code != "501") {
2366 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2367 }
2368 f_dlcx_ok(ep_1);
2369
2370 setverdict(pass);
2371 }
2372
2373 /* Create one connection in loopback mode, test if the RTP packets are
2374 * actually reflected */
2375 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2376 var RtpFlowData flow;
2377 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2378 var MgcpCallId call_id := '12250989'H;
2379 var RtpemStats stats;
2380
2381 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002382 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002383 flow.em.portnr := 10000;
2384 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2385
2386 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2387 f_sleep(1.0);
2388 f_flow_delete(RTPEM[0], ep, call_id);
2389
2390 stats := f_rtpem_stats_get(RTPEM[0]);
2391
2392 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2393 setverdict(fail);
2394 }
2395 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2396 setverdict(fail);
2397 }
2398
2399 f_rtpem_stats_err_check(stats);
2400
2401 setverdict(pass);
2402 }
2403
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002404 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2405 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2406 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2407 var template MgcpCommand cmd;
2408 var MgcpResponse resp;
2409 var MgcpCallId call_id := '1234'H;
2410 var MgcpConnectionId conn_id;
2411
2412 f_init(ep);
2413
2414 /* create the connection on the MGW */
2415 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2416 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2417 conn_id := extract_conn_id(resp);
2418
2419 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2420 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2421 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2422 valueof(ts_SDP_ptime(20)) });
2423 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2424
2425 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2426 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2427 }
2428 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2429 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2430 }
2431
2432 /* clean-up */
2433 f_dlcx_ok(ep, call_id);
2434 setverdict(pass);
2435 }
2436
2437 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2438 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2439 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2440 var template MgcpCommand cmd;
2441 var MgcpResponse resp;
2442 var MgcpCallId call_id := '1234'H;
2443 var MgcpConnectionId conn_id;
2444
2445 f_init(ep);
2446
2447 /* create the connection on the MGW */
2448 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2449 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2450 conn_id := extract_conn_id(resp);
2451
2452 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2453 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2454 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2455 valueof(ts_SDP_ptime(20)) });
2456 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2457
2458 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2459 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2460 }
2461 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2462 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2463 }
2464
2465 /* clean-up */
2466 f_dlcx_ok(ep, call_id);
2467 setverdict(pass);
2468 }
2469
Harald Welte00a067f2017-09-13 23:27:17 +02002470 control {
2471 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002472 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002473 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002474 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002475 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002476 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002477 execute(TC_crcx_early_bidir_mode());
2478 execute(TC_crcx_unsupp_param());
2479 execute(TC_crcx_missing_callid());
2480 execute(TC_crcx_missing_mode());
2481 execute(TC_crcx_unsupp_packet_intv());
2482 execute(TC_crcx_illegal_double_lco());
2483 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002484 execute(TC_crcx_wildcarded());
2485 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002486 execute(TC_mdcx_without_crcx());
2487 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02002488 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002489 execute(TC_mdcx_wildcarded());
2490 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002491 execute(TC_crcx_and_dlcx_ep_callid_connid());
2492 execute(TC_crcx_and_dlcx_ep_callid());
2493 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002494 execute(TC_crcx_and_dlcx_ep_callid_inval());
2495 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002496 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002497
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002498 execute(TC_crcx_osmux_wildcard());
2499 execute(TC_crcx_osmux_fixed());
2500 execute(TC_crcx_osmux_fixed_twice());
2501 execute(TC_one_crcx_receive_only_osmux());
2502 execute(TC_one_crcx_loopback_osmux());
2503 execute(TC_two_crcx_and_rtp_osmux());
2504 execute(TC_two_crcx_and_rtp_osmux_bidir());
2505 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2506 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2507
Harald Welte33d82162017-12-28 03:21:57 +01002508 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002509
2510 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002511
2512 execute(TC_one_crcx_receive_only_rtp());
2513 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002514 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02002515 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002516 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002517 execute(TC_two_crcx_diff_pt_and_rtp());
2518 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002519 execute(TC_two_crcx_mdcx_and_rtp());
2520 execute(TC_two_crcx_and_unsolicited_rtp());
2521 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002522 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002523 execute(TC_amr_oa_bwe_rtp_conversion());
2524 execute(TC_amr_oa_oa_rtp_conversion());
2525 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002526
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002527 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002528
2529 execute(TC_e1_crcx_and_dlcx_ep());
2530 execute(TC_e1_crcx_with_overlap());
2531 execute(TC_e1_crcx_loopback());
2532
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002533 execute(TC_crcx_mdcx_ip4());
2534 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002535 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2536 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2537 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2538 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2539 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2540 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2541 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2542 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02002543
2544 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
2545 * older versions of osmo-mgw. This eventually leads into
2546 * a failure of all subsequent testcases, so it is important
2547 * not to add new testcaes after this one. */
2548 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02002549 }
2550}