blob: 3aa7926495c364e0fba2c7848f30e10caa3a1176 [file] [log] [blame]
Harald Welte34b5a952019-05-27 11:54:11 +02001/* MGW (Media Gateway) test suite in TTCN-3
2 * (C) 2017-2018 Harald Welte <laforge@gnumonks.org>
3 * (C) 2018-2019 sysmocom - s.f.m.c. GmbH
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
Harald Welte00a067f2017-09-13 23:27:17 +020012module MGCP_Test {
Harald Weltef07c2862017-11-18 17:16:24 +010013 import from Osmocom_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +020014 import from MGCP_Types all;
Harald Welte4029e8c2017-11-23 22:00:42 +010015 import from MGCP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080016 import from SDP_Types all;
17 import from MGCP_CodecPort all;
18 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +010019 import from RTP_CodecPort all;
20 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020021 import from RTP_Emulation all;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020022 import from OSMUX_Types all;
23 import from OSMUX_CodecPort all;
24 import from OSMUX_CodecPort_CtrlFunct all;
25 import from OSMUX_Emulation all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080026 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010027 import from General_Types all;
28 import from Native_Functions all;
29 import from IPCP_Types all;
30 import from IP_Types all;
31 import from Osmocom_VTY_Functions all;
32 import from TELNETasp_PortType all;
Philipp Maier55b90542021-07-02 12:33:19 +020033 import from StatsD_Types all;
34 import from StatsD_CodecPort all;
35 import from StatsD_CodecPort_CtrlFunct all;
36 import from StatsD_Checker all;
Harald Welte00a067f2017-09-13 23:27:17 +020037
Philipp Maierbb7a01c2018-02-01 12:32:57 +010038 const charstring c_mgw_domain := "mgw";
39 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
40
Harald Welte21ba5572017-09-19 17:55:05 +080041 /* any variables declared in the component will be available to
42 * all functions that 'run on' the named component, similar to
43 * class members in C++ */
Philipp Maier55b90542021-07-02 12:33:19 +020044 type component dummy_CT extends StatsD_ConnHdlr {
Harald Welte3c6ebb92017-09-16 00:56:57 +080045 port MGCP_CODEC_PT MGCP;
46 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010047 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080048 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010049
Philipp Maier2321ef92018-06-27 17:52:04 +020050 var RTP_Emulation_CT vc_RTPEM[3];
51 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010052
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020053 var OSMUX_Emulation_CT vc_OsmuxEM;
54 port OsmuxEM_CTRL_PT OsmuxEM;
55
Philipp Maier6137c322019-02-20 16:13:41 +010056 port TELNETasp_PT MGWVTY;
Philipp Maier55b90542021-07-02 12:33:19 +020057
58 var StatsD_Checker_CT vc_STATSD;
Harald Welte00a067f2017-09-13 23:27:17 +020059 };
60
Harald Weltee1e18c52017-09-17 16:23:07 +080061 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
62 var MgcpTransId tid := int2str(g_trans_id);
63 g_trans_id := g_trans_id + 1;
64 return tid;
65 }
66
Harald Welte21ba5572017-09-19 17:55:05 +080067 /* all parameters declared here can be modified / overridden by
68 * the config file in the [MODULE_PARAMETERS] section. If no
69 * config file is used or the file doesn't specify them, the
70 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080071 modulepar {
72 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020073 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020074 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080075 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020076 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020077 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010078 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020079 PortNumber mp_local_osmux_port := 1985;
Philipp Maier55b90542021-07-02 12:33:19 +020080 PortNumber mp_mgw_statsd_port := 8125;
81 charstring mp_test_ip := "127.0.0.1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080082 }
83
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020084 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
85 /* Turn on conversion mode */
86 f_vty_enter_config(MGWVTY);
87 f_vty_transceive(MGWVTY, "mgcp");
88 if (osmux_on) {
89 f_vty_transceive(MGWVTY, "osmux on");
90 } else {
91 f_vty_transceive(MGWVTY, "osmux off");
92 }
93 f_vty_transceive(MGWVTY, "exit");
94 f_vty_transceive(MGWVTY, "exit");
95
96 }
97
98 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +010099 map(self:MGWVTY, system:MGWVTY);
100 f_vty_set_prompts(MGWVTY);
101 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200102
103 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +0100104 }
105
Harald Weltebb7523b2018-03-29 08:52:01 +0200106 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
107 runs on dummy_CT {
108 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
109 map(comp_ref:RTP, system:RTP);
110 map(comp_ref:RTCP, system:RTCP);
111 comp_ref.start(RTP_Emulation.f_main());
112 }
113
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200114 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
115 runs on dummy_CT {
116 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
117 map(comp_ref:OSMUX, system:OSMUX);
118 comp_ref.start(OSMUX_Emulation.f_main());
119 }
120
Harald Welte21ba5572017-09-19 17:55:05 +0800121 /* initialization function, called by each test case at the
122 * beginning, but 'initialized' variable ensures its body is
123 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200124 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800125 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100126 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200127
Harald Welteedc45c12017-11-18 19:15:05 +0100128 if (initialized == false) {
129 initialized := true;
130
131 /* some random number for the initial transaction id */
132 g_trans_id := float2int(rnd()*65535.0);
133 map(self:MGCP, system:MGCP_CODEC_PT);
134 /* connect the MGCP test port using the given
135 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
136 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200137 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 +0200138 if (not ispresent(res.connId)) {
139 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200140 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200141 }
Harald Welteedc45c12017-11-18 19:15:05 +0100142 g_mgcp_conn_id := res.connId;
143
Harald Weltebb7523b2018-03-29 08:52:01 +0200144 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
145 f_rtpem_init(vc_RTPEM[i], i);
146 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
147 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200148 if (osmux_on) {
149 f_osmuxem_init(vc_OsmuxEM);
150 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
151 }
Philipp Maier55b90542021-07-02 12:33:19 +0200152
153 f_init_statsd("VirtCallAgent", vc_STATSD, mp_test_ip, mp_mgw_statsd_port);
154 connect(self:STATSD_PROC, vc_STATSD:STATSD_PROC);
155
156 f_statsd_reset();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800157 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800158
Harald Welteedc45c12017-11-18 19:15:05 +0100159 if (isvalue(ep)) {
160 /* do a DLCX on all connections of the EP */
161 f_dlcx_ignore(valueof(ep));
162 }
Philipp Maier6137c322019-02-20 16:13:41 +0100163
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200164 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800165 }
166
Harald Welte00a067f2017-09-13 23:27:17 +0200167 testcase TC_selftest() runs on dummy_CT {
168 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 +0100169 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 +0200170 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
171 "I: 1\n" &
172 "\n" &
173 "v=0\r\n" &
174 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
175 "s=-\r\n" &
176 "c=IN IP4 0.0.0.0\r\n" &
177 "t=0 0\r\n" &
178 "m=audio 0 RTP/AVP 126\r\n" &
179 "a=rtpmap:126 AMR/8000\r\n" &
180 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100181 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 +0200182 "M: sendrecv\r" &
183 "C: 2\r\n" &
184 "I: 1\r\n" &
185 "L: p:20, a:AMR, nt:IN\r\n" &
186 "\n" &
187 "v=0\r\n" &
188 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800189 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200190 "c=IN IP4 0.0.0.0\r\n" &
191 "t=0 0\r\n" &
192 "m=audio 4441 RTP/AVP 99\r\n" &
193 "a=rtpmap:99 AMR/8000\r\n" &
194 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800195 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200196
197 log(c_auep);
198 log(dec_MgcpCommand(c_auep));
199
200 log(c_mdcx3);
201 log(dec_MgcpCommand(c_mdcx3));
202
203 log(c_mdcx3_ret);
204 log(dec_MgcpResponse(c_mdcx3_ret));
205
206 log(c_mdcx4);
207 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800208
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100209 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
210 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 +0800211
212 log(c_crcx510_ret);
213 log(dec_MgcpResponse(c_crcx510_ret));
214 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100215
216 /* We didn't encounter any DTE, so pass the test */
217 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800218 }
219
Harald Weltee636afd2017-09-17 16:24:09 +0800220 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100221 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800222 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
223 * - CRCX with remote session description and without
224 *
225 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100226 * x packetization != 20ms
227 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800228 * x unsupported mode (517)
229 * x bidirectional mode before RemoteConnDesc: 527
230 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100231 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800232 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
233 */
234
Harald Welte21ba5572017-09-19 17:55:05 +0800235 /* build a receive template for receiving a MGCP message. You
236 * pass the MGCP response template in, and it will generate an
237 * MGCP_RecvFrom template that can match the primitives arriving on the
238 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800239 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
240 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100241 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200242 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800243 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200244 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800245 locPort := mp_local_udp_port,
246 msg := { response := resp }
247 }
248 return mrf;
249 }
250
251 /* Send a MGCP request + receive a (matching!) response */
252 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
253 var MgcpMessage msg := { command := valueof(cmd) };
254 resp.line.trans_id := cmd.line.trans_id;
255 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800256 var MGCP_RecvFrom mrf;
257 timer T := 5.0;
258
Harald Welte55015362017-11-18 16:02:42 +0100259 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800260 T.start;
261 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800262 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200263 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
264 setverdict(fail, "Response didn't match template");
265 mtc.stop;
266 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800267 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200268 [] T.timeout {
269 setverdict(fail, "Timeout waiting for response to ", cmd);
270 mtc.stop;
271 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800272 }
273 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800274
275 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
276 return mrf.msg.response;
277 } else {
278 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
279 return r;
280 }
Harald Welte00a067f2017-09-13 23:27:17 +0200281 }
282
Harald Welteba62c8c2017-11-18 18:26:49 +0100283 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
284 var integer i;
285 for (i := 0; i < lengthof(resp.params); i := i + 1) {
286 var MgcpParameter par := resp.params[i];
287 if (par.code == "I") {
288 return str2hex(par.val);
289 }
290 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200291 setverdict(fail, "Could not find conn id for MgcpReponse");
292 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100293 return '00000000'H;
294 }
295
Harald Welte10889c12017-11-18 19:40:31 +0100296 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
297 template MgcpCallId call_id := omit,
298 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100299 var template MgcpCommand cmd;
300 var MgcpResponse resp;
301 var template MgcpResponse rtmpl := {
302 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100303 code := ret_code,
304 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100305 },
306 params := *,
307 sdp := *
308 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100309 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100310 resp := mgcp_transceive_mgw(cmd, rtmpl);
311 }
312
Harald Welte10889c12017-11-18 19:40:31 +0100313 /* Send DLCX and expect OK response */
314 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
315 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100316 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100317 }
318
Harald Welteba62c8c2017-11-18 18:26:49 +0100319 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100320 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100321 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100322 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100323 }
324
Harald Weltebb7523b2018-03-29 08:52:01 +0200325 type record HostPort {
326 charstring hostname,
327 integer portnr optional
328 }
329 type record RtpFlowData {
330 HostPort em, /* emulation side */
331 HostPort mgw, /* mgw side */
332 uint7_t pt,
333 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200334 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100335 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200336 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
337 MgcpOsmuxCID osmux_cid optional,
338 MgcpOsmuxCID osmux_cid_response optional,
339 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100340 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200341 }
342
Philipp Maier2321ef92018-06-27 17:52:04 +0200343 /* Create an RTP flow (bidirectional, or receive-only) */
344 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 +0200345 boolean one_phase := true)
346 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200347 var template MgcpCommand cmd;
348 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100349 var SDP_attribute_list attributes;
350
351 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
352 if (isvalue(flow.fmtp)) {
353 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
354 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200355
356 /* bind local RTP emulation socket */
357 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
358
Philipp Maier28bb8292018-07-20 17:09:17 +0200359 /* configure rtp-emulation */
360 if (ispresent(flow.rtp_cfg)) {
361 f_rtpem_configure(pt, flow.rtp_cfg);
362 } else {
363 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
364 rtp_cfg.tx_payload_type := flow.pt
365 f_rtpem_configure(pt, rtp_cfg);
366 }
367
Harald Weltebb7523b2018-03-29 08:52:01 +0200368 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200369 /* Connect flow to MGW using a CRCX that also contains an SDP
370 * part that tells the MGW where we are listening for RTP streams
371 * that come from the MGW. We get a fully working connection in
372 * one go. */
373
374 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200375 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100376 flow.em.portnr, { int2str(flow.pt) }, attributes);
377
Harald Weltebb7523b2018-03-29 08:52:01 +0200378 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
379 flow.mgcp_conn_id := extract_conn_id(resp);
380 /* extract port number from response */
381 flow.mgw.portnr :=
382 resp.sdp.media_list[0].media_field.ports.port_number;
383 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200384 /* Create a half-open connection only. We do not tell the MGW
385 * where it can send RTP streams to us. This means this
386 * connection will only be able to receive but can not send
387 * data back to us. In order to turn the connection in a fully
388 * bi-directional one, a separate MDCX is needed. */
389
390 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200391 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
392 flow.mgcp_conn_id := extract_conn_id(resp);
393 /* extract MGW-side port number from response */
394 flow.mgw.portnr :=
395 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200396 }
397 /* finally, connect the emulation-side RTP socket to the MGW */
398 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
399 }
400
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200401 /* Create an Osmux flow (bidirectional, or receive-only) */
402 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
403 boolean one_phase := true)
404 runs on dummy_CT {
405 var template MgcpCommand cmd;
406 var MgcpResponse resp;
407 var SDP_attribute_list attributes;
408 var OsmuxTxHandle tx_hdl;
409 var OsmuxRxHandle rx_hdl;
410 var charstring cid_response;
411 var OsmuxCID cid_resp_parsed
412
413 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
414 if (isvalue(flow.fmtp)) {
415 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
416 }
417
418 /* bind local Osmux emulation socket */
419 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
420
421 /* configure osmux-emulation */
422 if (ispresent(flow.osmux_cfg)) {
423 f_osmuxem_configure(pt, flow.osmux_cfg);
424 } else {
425 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
426 f_osmuxem_configure(pt, osmux_cfg);
427 flow.osmux_cfg := osmux_cfg
428 }
429
430 if (one_phase) {
431 /* Connect flow to MGW using a CRCX that also contains an SDP
432 * part that tells the MGW where we are listening for Osmux streams
433 * that come from the MGW. We get a fully working connection in
434 * one go. */
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200435 if (flow.osmux_cid != -1) {
436 /* We may still want to negotiate osmux CID later at MDCX */
437 rx_hdl := c_OsmuxemDefaultRxHandle;
438 rx_hdl.cid := flow.osmux_cid;
439 f_osmuxem_register_rxhandle(pt, rx_hdl);
440 flow.osmux_cid_sent := true;
441 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200442 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
443 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
444 flow.em.portnr, { int2str(flow.pt) }, attributes);
445 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
446 flow.mgcp_conn_id := extract_conn_id(resp);
447 /* extract port number from response */
448 flow.mgw.portnr :=
449 resp.sdp.media_list[0].media_field.ports.port_number;
450 } else {
451 /* Create a half-open connection only. We do not tell the MGW
452 * where it can send Osmux streams to us. This means this
453 * connection will only be able to receive but can not send
454 * data back to us. In order to turn the connection in a fully
455 * bi-directional one, a separate MDCX is needed. */
456
457 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
458 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
459
460 flow.mgcp_conn_id := extract_conn_id(resp);
461 /* extract MGW-side port number from response */
462 flow.mgw.portnr :=
463 resp.sdp.media_list[0].media_field.ports.port_number;
464 }
465
466 /* extract Osmux CID we got assigned by the MGW */
467 var MgcpMessage resp_msg := {
468 response := resp
469 }
470
471 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
472 setverdict(fail, "No Osmux CID in MGCP response", resp);
473 mtc.stop;
474 }
475
476 /* Make sure response is no wildcard */
477 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
478 if (flow.osmux_cid_response == -1) {
479 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
480 mtc.stop;
481 }
482 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
483 f_osmuxem_register_txhandle(pt, tx_hdl);
484
485 /* finally, connect the emulation-side RTP socket to the MGW */
486 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
487 }
488
Philipp Maier2321ef92018-06-27 17:52:04 +0200489 /* Modify an existing RTP flow */
490 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
491 runs on dummy_CT {
492 var template MgcpCommand cmd;
493 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100494 var SDP_attribute_list attributes;
495
496 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
497 if (isvalue(flow.fmtp)) {
498 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
499 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200500
501 /* rebind local RTP emulation socket to the new address */
502 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
503
Philipp Maier28bb8292018-07-20 17:09:17 +0200504 /* reconfigure rtp-emulation */
505 if (ispresent(flow.rtp_cfg)) {
506 f_rtpem_configure(pt, flow.rtp_cfg);
507 } else {
508 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
509 rtp_cfg.tx_payload_type := flow.pt
510 f_rtpem_configure(pt, rtp_cfg);
511 }
512
Philipp Maier2321ef92018-06-27 17:52:04 +0200513 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
514 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
515 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100516 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200517 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
518
519 /* extract MGW-side port number from response. (usually this
520 * will not change, but thats is up to the MGW) */
521 flow.mgw.portnr :=
522 resp.sdp.media_list[0].media_field.ports.port_number;
523
524 /* reconnect the emulation-side RTP socket to the MGW */
525 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
526 }
527
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200528 /* Modify an existing Osmux flow */
529 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
530 runs on dummy_CT {
531 var template MgcpCommand cmd;
532 var MgcpResponse resp;
533 var SDP_attribute_list attributes;
534 var OsmuxRxHandle rx_hdl;
535 var charstring cid_response;
536 var OsmuxCID cid_resp_parsed
537
538 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
539 if (isvalue(flow.fmtp)) {
540 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
541 }
542
543 /* rebind local Osmux emulation socket to the new address */
544 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
545
546 /* configure osmux-emulation */
547 if (ispresent(flow.osmux_cfg)) {
548 f_osmuxem_configure(pt, flow.osmux_cfg);
549 } else {
550 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
551 f_osmuxem_configure(pt, osmux_cfg);
552 }
553
554 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
555 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
556 rx_hdl := c_OsmuxemDefaultRxHandle;
557 rx_hdl.cid := flow.osmux_cid;
558 f_osmuxem_register_rxhandle(pt, rx_hdl);
559 flow.osmux_cid_sent := true;
560 }
561
562 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
563 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
564 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
565 flow.em.portnr, { int2str(flow.pt) }, attributes);
566 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
567
568 /* extract MGW-side port number from response. (usually this
569 * will not change, but thats is up to the MGW) */
570 flow.mgw.portnr :=
571 resp.sdp.media_list[0].media_field.ports.port_number;
572
573 /* extract Osmux CID we got assigned by the MGW */
574 var MgcpMessage resp_msg := {
575 response := resp
576 }
577
578 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
579 setverdict(fail, "No Osmux CID in MGCP response", resp);
580 mtc.stop;
581 }
582
583 /* Make sure response is no wildcard */
584 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
585 if (cid_resp_parsed == -1) {
586 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
587 mtc.stop;
588 }
589 if (cid_resp_parsed != flow.osmux_cid_response) {
590 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
591 mtc.stop;
592 }
593
594 /* reconnect the emulation-side Osmux socket to the MGW */
595 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
596 }
597
Philipp Maier2321ef92018-06-27 17:52:04 +0200598 /* Delete an existing RTP flow */
599 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
600 runs on dummy_CT {
601 var template MgcpCommand cmd;
602 var MgcpResponse resp;
603
604 /* Switch off RTP flow */
605 f_rtpem_mode(pt, RTPEM_MODE_NONE);
606
607 /* Delete connection on MGW (if needed) */
608 if (isvalue(call_id) and isvalue(ep)) {
609 f_sleep(0.1);
610 f_dlcx_ok(valueof(ep), call_id);
611 }
612 }
613
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200614 /* Delete an existing Osmux flow */
615 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
616 runs on dummy_CT {
617 var template MgcpCommand cmd;
618 var MgcpResponse resp;
619
620 /* Switch off Osmux flow */
621 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
622
623 /* Delete connection on MGW (if needed) */
624 if (isvalue(call_id) and isvalue(ep)) {
625 f_sleep(0.1);
626 f_dlcx_ok(valueof(ep), call_id);
627 }
628 }
629
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100630 function f_crcx(charstring ep_prefix) runs on dummy_CT {
631 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800632 var template MgcpCommand cmd;
633 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100634 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800635
Harald Welteedc45c12017-11-18 19:15:05 +0100636 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800637
Harald Welteba62c8c2017-11-18 18:26:49 +0100638 /* create the connection on the MGW */
639 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100640 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100641 extract_conn_id(resp);
642
643 /* clean-up */
644 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100645 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100646
Philipp Maier45635f42018-06-05 17:28:02 +0200647 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
648 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
649 var template MgcpCommand cmd;
650 var MgcpResponse resp;
651 var MgcpCallId call_id := '1234'H;
652
653 f_init(ep);
654
655 /* create the connection on the MGW */
656 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
657 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
658 extract_conn_id(resp);
659
660 /* clean-up */
661 f_dlcx_ok(ep, call_id);
662
663 /* See also OS#2658: Even when we omit the LCO information, we
664 expect the MGW to pick a sane payload type for us. This
665 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200666 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200667 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200668 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200669 }
670
671 /* See also OS#2658: We also expect the MGW to assign a port
672 number to us. */
673 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
674 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200675 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200676 }
677 }
678
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200679 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
680 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
681 var template MgcpCommand cmd;
682 var MgcpResponse resp;
683 var MgcpCallId call_id := '1234'H;
684 var charstring cid_response;
685
686 if (run_init) {
687 f_init(ep, true);
688 }
689
690 /* create the connection on the MGW */
691 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
692 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
693 extract_conn_id(resp);
694
695 /* extract Osmux CID we got assigned by the MGW */
696 var MgcpMessage resp_msg := {
697 response := resp
698 }
699
700 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
701 setverdict(fail, "No Osmux CID in MGCP response", resp);
702 mtc.stop;
703 }
704
705 /* Make sure response is no wildcard */
706 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
707 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
708 mtc.stop;
709 }
710
711 /* clean-up */
712 f_dlcx_ok(ep, call_id);
713 }
714
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100715 /* test valid CRCX without SDP */
716 testcase TC_crcx() runs on dummy_CT {
717 f_crcx(c_mgw_ep_rtpbridge);
718 setverdict(pass);
719 }
720
Philipp Maier45635f42018-06-05 17:28:02 +0200721 /* test valid CRCX without SDP and LCO */
722 testcase TC_crcx_no_lco() runs on dummy_CT {
723 f_crcx_no_lco(c_mgw_ep_rtpbridge);
724 setverdict(pass);
725 }
726
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100727 /* test valid CRCX without SDP (older method without endpoint prefix) */
728 testcase TC_crcx_noprefix() runs on dummy_CT {
729 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800730 setverdict(pass);
731 }
732
733 /* test CRCX with unsupported mode, expect 517 */
734 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
735 var template MgcpCommand cmd;
736 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100737 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100738 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800739 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
740
Harald Welteedc45c12017-11-18 19:15:05 +0100741 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800742
Harald Welteba62c8c2017-11-18 18:26:49 +0100743 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800744 resp := mgcp_transceive_mgw(cmd, rtmpl);
745 setverdict(pass);
746 }
747
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200748 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
749 testcase TC_crcx_osmo_ign() runs on dummy_CT {
750 var template MgcpCommand cmd;
751 var MgcpResponse resp;
752 var MgcpEndpoint ep := "7@" & c_mgw_domain;
753 var MgcpCallId call_id := '3'H;
754
755 f_init(ep);
756
757 /* CRCX 1 7@mgw MGCP 1.0
758 C: 3
759 L: p:20, a:GSM-EFR, nt:IN
760 M: recvonly
761 X-Osmo-IGN: C
762 */
763
764 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
765 cmd.params := {ts_MgcpParCallId(call_id),
766 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
767 t_MgcpParConnMode("recvonly"),
768 t_MgcpParOsmoIGN("C")};
769 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
770 extract_conn_id(resp);
771
772 /* clean-up */
773 f_dlcx_ok(ep, call_id);
774 setverdict(pass);
775 }
776
Harald Welte21ba5572017-09-19 17:55:05 +0800777 /* test CRCX with early bi-directional mode, expect 527 as
778 * bi-diretional media can only be established once both local and
779 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800780 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
781 var template MgcpCommand cmd;
782 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100783 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100784 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800785 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
786
Harald Welteedc45c12017-11-18 19:15:05 +0100787 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800788
Harald Welteba62c8c2017-11-18 18:26:49 +0100789 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800790 resp := mgcp_transceive_mgw(cmd, rtmpl);
791 setverdict(pass);
792 }
793
794 /* test CRCX with unsupported Parameters */
795 testcase TC_crcx_unsupp_param() runs on dummy_CT {
796 var template MgcpCommand cmd;
797 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100798 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100799 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800800 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
801
Harald Welteedc45c12017-11-18 19:15:05 +0100802 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800803
Harald Welteba62c8c2017-11-18 18:26:49 +0100804 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100805 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
806 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
807
Harald Weltee636afd2017-09-17 16:24:09 +0800808 resp := mgcp_transceive_mgw(cmd, rtmpl);
809 setverdict(pass);
810 }
811
812 /* test CRCX with missing CallId */
813 testcase TC_crcx_missing_callid() runs on dummy_CT {
814 var template MgcpCommand cmd;
815 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100816 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100817 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800818
Harald Welteedc45c12017-11-18 19:15:05 +0100819 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800820
Harald Welteba62c8c2017-11-18 18:26:49 +0100821 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800822 cmd.params := {
823 t_MgcpParConnMode("recvonly"),
824 t_MgcpParLocConnOpt("p:20")
825 }
826 resp := mgcp_transceive_mgw(cmd, rtmpl);
827 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100828
Harald Weltee636afd2017-09-17 16:24:09 +0800829 }
830
831 /* test CRCX with missing Mode */
832 testcase TC_crcx_missing_mode() runs on dummy_CT {
833 var template MgcpCommand cmd;
834 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100835 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100836 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100837 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800838
Harald Welteedc45c12017-11-18 19:15:05 +0100839 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800840
Harald Welteba62c8c2017-11-18 18:26:49 +0100841 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800842 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100843 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800844 t_MgcpParLocConnOpt("p:20")
845 }
846 resp := mgcp_transceive_mgw(cmd, rtmpl);
847 setverdict(pass);
848 }
849
850 /* test CRCX with unsupported packetization interval */
851 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
852 var template MgcpCommand cmd;
853 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100854 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100855 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100856 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800857
Harald Welteedc45c12017-11-18 19:15:05 +0100858 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800859
Harald Welteba62c8c2017-11-18 18:26:49 +0100860 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100861 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800862 resp := mgcp_transceive_mgw(cmd, rtmpl);
863 setverdict(pass);
864 }
865
866 /* test CRCX with illegal double presence of local connection option */
867 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
868 var template MgcpCommand cmd;
869 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100870 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100871 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800872 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
873
Harald Welteedc45c12017-11-18 19:15:05 +0100874 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800875
Harald Welteba62c8c2017-11-18 18:26:49 +0100876 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100877 /* p:20 is permitted only once and not twice! */
878 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800879 resp := mgcp_transceive_mgw(cmd, rtmpl);
880 setverdict(pass);
881 }
882
883 /* test valid CRCX with valid SDP */
884 testcase TC_crcx_sdp() runs on dummy_CT {
885 var template MgcpCommand cmd;
886 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100887 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100888 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800889
Harald Welteedc45c12017-11-18 19:15:05 +0100890 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800891
Harald Welteba62c8c2017-11-18 18:26:49 +0100892 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800893 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
894 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
895 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100896 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100897
898 /* clean-up */
899 f_dlcx_ok(ep, call_id);
900
Harald Weltee636afd2017-09-17 16:24:09 +0800901 setverdict(pass);
902 }
903
Philipp Maier5e06cee2018-02-01 18:28:08 +0100904 /* test valid wildcarded CRCX */
905 testcase TC_crcx_wildcarded() runs on dummy_CT {
906 var template MgcpCommand cmd;
907 var MgcpResponse resp;
908 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
909 var MgcpCallId call_id := '1234'H;
910 var MgcpEndpoint ep_assigned;
911 f_init();
912
913 /* create the connection on the MGW */
914 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
915 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
916 extract_conn_id(resp);
917
918 /* extract endpoint name we got assigned by the MGW */
919 var MgcpMessage resp_msg := {
920 response := resp
921 }
922 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
923 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200924 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100925 }
926
927 /* clean-up */
928 f_dlcx_ok(ep_assigned, call_id);
929
930 setverdict(pass);
931 }
932
933 /* test valid wildcarded CRCX */
934 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maierc60e8472020-07-08 12:57:13 +0200935 const integer n_endpoints := 31;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100936 var integer i;
937 var template MgcpCommand cmd;
938 var MgcpResponse resp;
939 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
940 var MgcpCallId call_id := '1234'H;
941 var MgcpEndpoint ep_assigned[n_endpoints];
942 f_init();
943
944 /* Exhaust all endpoint resources on the virtual trunk */
945 for (i := 0; i < n_endpoints; i := i+1) {
946 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
947 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
948
949 /* Make sure we got a connection id */
950 extract_conn_id(resp);
951
952 var MgcpMessage resp_msg := {
953 response := resp
954 }
955 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
956 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200957 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100958 }
959 }
960
961 /* Try to allocate one more endpoint, which should fail */
962 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
963 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
964 resp := mgcp_transceive_mgw(cmd, rtmpl);
965 setverdict(pass);
966
967 /* clean-up */
968 for (i := 0; i < n_endpoints; i := i+1) {
969 f_dlcx_ok(ep_assigned[i], call_id);
970 }
971 setverdict(pass);
972 }
973
Harald Weltee636afd2017-09-17 16:24:09 +0800974 /* TODO: various SDP related bits */
975
976
977 /* TODO: CRCX with X-Osmux */
978 /* TODO: double CRCX without force_realloc */
979
980 /* TODO: MDCX (various) */
981
982 /* TODO: MDCX without CRCX first */
983 testcase TC_mdcx_without_crcx() runs on dummy_CT {
984 var template MgcpCommand cmd;
985 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100986 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100987 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800988 var template MgcpResponse rtmpl := {
989 line := {
990 /* TODO: accept/enforce better error? */
991 code := "400",
992 string := ?
993 },
994 params:= { },
995 sdp := omit
996 };
997
Harald Welteedc45c12017-11-18 19:15:05 +0100998 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800999
Harald Welte2bcfd3a2017-11-18 22:14:35 +01001000 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001001 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1002 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1003 valueof(ts_SDP_ptime(20)) });
1004 resp := mgcp_transceive_mgw(cmd, rtmpl);
1005 setverdict(pass);
1006 }
1007
1008 /* DLCX without CRCX first */
1009 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1010 var template MgcpCommand cmd;
1011 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001012 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001013 var template MgcpResponse rtmpl := {
1014 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001015 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001016 string := ?
1017 },
1018 params:= { },
1019 sdp := omit
1020 };
1021
Harald Welteedc45c12017-11-18 19:15:05 +01001022 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001023
Harald Welteedc45c12017-11-18 19:15:05 +01001024 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001025 resp := mgcp_transceive_mgw(cmd, rtmpl);
1026 setverdict(pass);
1027 }
1028
Philipp Maier21c1cff2021-07-20 14:22:53 +02001029 /* DLCX to non existing endpoint */
1030 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1031 var template MgcpCommand cmd;
1032 var MgcpResponse resp;
1033 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "AA@" & c_mgw_domain;
1034 var template MgcpResponse rtmpl := {
1035 line := {
1036 code := ("500"),
1037 string := ?
1038 },
1039 params:= { },
1040 sdp := omit
1041 };
1042
1043 f_init(ep);
1044
1045 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1046 resp := mgcp_transceive_mgw(cmd, rtmpl);
1047 setverdict(pass);
1048 }
1049
Philipp Maier8a3dc922018-02-02 14:55:12 +01001050 /* test valid wildcarded MDCX */
1051 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1052 /* Note: A wildcarded MDCX is not allowed, so we expect the
1053 * MGW to reject this request */
1054 var template MgcpCommand cmd;
1055 var MgcpResponse resp;
1056 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1057 var MgcpCallId call_id := '1225'H;
1058 var template MgcpResponse rtmpl := {
1059 line := {
1060 /* TODO: accept/enforce better error? */
1061 code := "507",
1062 string := ?
1063 },
1064 params:= { },
1065 sdp := omit
1066 };
1067
1068 f_init(ep);
1069
1070 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1071 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1072 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1073 valueof(ts_SDP_ptime(20)) });
1074 resp := mgcp_transceive_mgw(cmd, rtmpl);
1075 setverdict(pass);
1076 }
1077
1078 /* test valid wildcarded DLCX */
1079 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001080 var template MgcpCommand cmd;
1081 var MgcpResponse resp;
1082 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001083 const integer n_endpoints := 31;
1084 var integer i;
1085 var MgcpCallId call_id := '1234'H;
1086 var StatsDExpects expect;
1087 f_init(ep);
1088
1089 /* Allocate a few endpoints */
1090 for (i := 0; i < n_endpoints; i := i+1) {
1091 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1092 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1093 }
1094
1095 expect := {
1096 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := n_endpoints, max := n_endpoints}
1097 };
1098 f_statsd_expect(expect);
1099
1100 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001101 var template MgcpResponse rtmpl := {
1102 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001103 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001104 string := ?
1105 },
1106 params:= { },
1107 sdp := omit
1108 };
Philipp Maier55b90542021-07-02 12:33:19 +02001109 cmd := ts_DLCX(get_next_trans_id(), ep);
1110 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001111
Philipp Maier55b90542021-07-02 12:33:19 +02001112 /* The stats reporter collects multiple samples during the reporting interval and
1113 * reports the highest back to the user. This means we will not immediately get
1114 * the 0 endpoints but an intermediate result instead. */
1115 expect := {
1116 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := n_endpoints}
1117 };
1118 f_statsd_expect(expect);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001119
Philipp Maier55b90542021-07-02 12:33:19 +02001120 /* The second interval must resturn a result with 0 endpoints in use. */
1121 expect := {
1122 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1123 };
1124 f_statsd_expect(expect);
1125
Philipp Maier8a3dc922018-02-02 14:55:12 +01001126 setverdict(pass);
1127 }
1128
Harald Welte79181ff2017-11-18 19:26:11 +01001129 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1130 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1131 var template MgcpCommand cmd;
1132 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001133 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001134 var MgcpCallId call_id := '51234'H;
1135
1136 f_init(ep);
1137
1138 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1139 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1140
1141 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1142
1143 setverdict(pass);
1144 }
1145
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001146 /* test valid CRCX without SDP */
1147 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1148 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1149 setverdict(pass);
1150 }
1151
1152 /* test valid CRCX without SDP */
1153 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1154 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1155 setverdict(pass);
1156 }
1157
1158 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1159 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1160 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1161 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1162 setverdict(pass);
1163 }
1164
1165 /* Create one half open connection in receive-only mode. The MGW must accept
1166 * the packets but must not send any. */
1167 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1168 var RtpFlowData flow;
1169 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1170 var MgcpCallId call_id := '1225'H;
1171 var OsmuxemStats stats;
1172 var OsmuxTxHandle tx_hdl;
1173
1174 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001175 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001176 flow.em.portnr := mp_local_osmux_port;
1177 flow.osmux_cid := -1;
1178 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1179
1180 /* create a transmitter not yet known by MGW */
1181 tx_hdl := valueof(t_TxHandleAMR590(2));
1182 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1183
1184 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1185 f_sleep(1.0);
1186 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1187
1188 stats := f_osmuxem_stats_get(OsmuxEM);
1189
1190 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1191 setverdict(fail);
1192 }
1193 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1194 setverdict(fail);
1195 }
1196
1197 f_osmuxem_stats_err_check(stats);
1198
1199 setverdict(pass);
1200 }
1201
1202 /* Create one connection in loopback mode, test if the Osmux packets are
1203 * actually reflected */
1204 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1205 var RtpFlowData flow;
1206 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1207 var MgcpCallId call_id := '1225'H;
1208 var OsmuxemStats stats;
1209 var OsmuxTxHandle tx_hdl;
1210
1211 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001212 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001213 flow.em.portnr := mp_local_osmux_port;
1214 flow.osmux_cid := 2;
1215 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1216
1217 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1218 f_sleep(1.0);
1219
1220 /* Switch off both Tx, wait to receive delayed frames from MGW */
1221 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1222 f_sleep(0.1);
1223 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1224
1225 stats := f_osmuxem_stats_get(OsmuxEM);
1226
1227 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1228 setverdict(fail);
1229 }
1230 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1231 setverdict(fail);
1232 }
1233
1234 f_osmuxem_stats_err_check(stats);
1235
1236 setverdict(pass);
1237 }
1238
1239 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1240 * must match the reception statistics on the other side and vice versa. The
1241 * user may also supply a tolerance value (number of packets) when deviations
1242 * are acceptable */
1243 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1244 var integer plen;
1245
1246 log("stats A: ", a);
1247 log("stats B: ", b);
1248 log("tolerance: ", tolerance, " packets");
1249 log("batch_size: ", batch_size, " packets");
1250
1251 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1252
1253 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1254 return false;
1255 }
1256
1257 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1258 return false;
1259 }
1260
1261 if(a.num_pkts_tx > 0) {
1262 plen := a.bytes_payload_tx / a.num_pkts_tx;
1263 } else {
1264 plen := 0;
1265 }
1266
1267 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1268 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1269 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1270 return false;
1271 }
1272
1273 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) {
1274 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1275 return false;
1276 }
1277
1278 return true;
1279 }
1280
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001281 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1282 charstring local_ip_rtp, charstring remote_ip_rtp,
1283 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001284 var RtpFlowData flow[2];
1285 var RtpemStats stats_rtp;
1286 var OsmuxemStats stats_osmux;
1287 var MgcpResponse resp;
1288 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1289 var MgcpCallId call_id := '1226'H;
1290 var integer tolerance := 0;
1291
1292 f_init(ep, true);
1293
1294 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001295 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001296 flow[0].rtp_cfg := c_RtpemDefaultCfg
1297 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1298 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1299 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);
1300 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1301 /* bind local RTP emulation sockets */
1302 flow[0].em.portnr := 10000;
1303 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1304
1305 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001306 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001307 flow[1].em.portnr := mp_local_osmux_port;
1308 flow[1].osmux_cid := 2;
1309 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1310 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1311
1312 if (bidir) {
1313 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1314 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1315
1316 /* Note: When we test bidirectional we may
1317 * loose packets during switch off because
1318 * both ends are transmitting and we only
1319 * can switch them off one by one. */
1320 tolerance := 3;
1321 } else {
1322 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1323 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1324 }
1325
1326 f_sleep(1.0);
1327
1328 /* Switch off both Tx, wait to receive delayed frames from MGW */
1329 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1330 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1331 f_sleep(0.1);
1332
1333 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1334 f_flow_delete(RTPEM[1]);
1335
1336 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1337 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1338 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1339 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1340 mtc.stop;
1341 }
1342
1343 f_rtpem_stats_err_check(stats_rtp);
1344 f_osmuxem_stats_err_check(stats_osmux);
1345
1346 setverdict(pass);
1347 }
1348
1349 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1350 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001351 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1352 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001353 }
1354
1355 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1356 * exchange some data in both directions */
1357 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001358 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1359 mp_local_ipv4, mp_remote_ipv4);
1360 }
1361
1362 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1363 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1364 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1365 mp_local_ipv6, mp_remote_ipv6);
1366 }
1367 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1368 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1369 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1370 mp_local_ipv6, mp_remote_ipv6);
1371 }
1372 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1373 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1374 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1375 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001376 }
1377
1378
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001379 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1380 charstring local_ip_rtp, charstring remote_ip_rtp,
1381 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001382 var RtpFlowData flow[2];
1383 var RtpemStats stats_rtp;
1384 var OsmuxemStats stats_osmux;
1385 var MgcpResponse resp;
1386 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1387 var MgcpCallId call_id := '1227'H;
1388 var integer num_pkts_tx[2];
1389 var integer temp;
1390
1391 f_init(ep, true);
1392
1393 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001394 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001395 flow[0].rtp_cfg := c_RtpemDefaultCfg
1396 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1397 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1398 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);
1399 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1400 /* bind local RTP emulation sockets */
1401 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001402 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001403
1404
1405 /* Create the second connection. This connection will be also
1406 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001407 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001408 flow[1].em.portnr := mp_local_osmux_port;
1409 if (crcx_osmux_wildcard) {
1410 flow[1].osmux_cid := -1;
1411 } else {
1412 flow[1].osmux_cid := 2;
1413 }
1414 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001415 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001416
1417
1418 /* The first leg starts transmitting */
1419 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1420 f_sleep(0.5);
1421 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1422 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1423 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1424 mtc.stop;
1425 }
1426 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1427 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1428 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1429 mtc.stop;
1430 }
1431
1432 /* The second leg starts transmitting a little later */
1433 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1434 f_sleep(1.0);
1435 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1436 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1437 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1438 mtc.stop;
1439 }
1440 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1441 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1442 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1443 mtc.stop;
1444 }
1445
1446 /* The first leg will now be switched into bidirectional
1447 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001448 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1449 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1450 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001451 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1452 f_sleep(0.5);
1453 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1454 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1455 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1456 mtc.stop;
1457 }
1458 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1459 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1460 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1461 mtc.stop;
1462 }
1463
1464 /* When the second leg is switched into bidirectional mode
1465 * as well, then the MGW will connect the two together and
1466 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001467 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1468 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001469 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001470
1471 if (crcx_osmux_wildcard) {
1472 /* For now we must set same CID as the MGW recvCID,
1473 * having sendCID!=recvCID is not yet supported. */
1474 flow[1].osmux_cid := flow[1].osmux_cid_response;
1475 }
1476 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1477 f_sleep(2.0);
1478
1479 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1480 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1481
1482 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1483 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1484 log("stats_rtp: ", stats_rtp);
1485 log("stats_osmux: ", stats_osmux);
1486 log("old_rtp_tx: ", num_pkts_tx[0]);
1487 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1488 mtc.stop;
1489 }
1490
1491 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1492 if (temp > 3 or temp < -3) {
1493 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1494 mtc.stop;
1495 }
1496
1497 f_rtpem_stats_err_check(stats_rtp);
1498 f_osmuxem_stats_err_check(stats_osmux);
1499
1500 /* Tear down */
1501 f_flow_delete(RTPEM[0]);
1502 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1503 setverdict(pass);
1504 }
1505
1506 /* create one RTP and one OSmux emulations and pass data in both
1507 directions. Create CRCX with wildcard Osmux CID and set it later
1508 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1509 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001510 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1511 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001512 }
1513
1514 /* create one RTP and one OSmux emulations and pass data in both
1515 directions. Create CRCX with fixed Osmux CID and keep it during
1516 MDCX. This is similar to how BSC sets up the call in AoIP. */
1517 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001518 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1519 mp_local_ipv4, mp_remote_ipv4);
1520 }
1521
1522 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1523 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1524 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1525 mp_local_ipv6, mp_remote_ipv6);
1526 }
1527 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1528 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1529 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1530 mp_local_ipv6, mp_remote_ipv6);
1531 }
1532 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1533 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1534 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1535 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001536 }
1537
Harald Welte646ecdb2017-12-28 03:21:57 +01001538 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1539 var template MgcpCommand cmd;
1540 var MgcpResponse resp;
1541
1542 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1543 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1544
1545 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1546
1547 setverdict(pass);
1548 }
1549
1550 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1551 var MgcpEndpoint ep;
1552 var MgcpCallId call_id;
1553 var integer ep_nr;
1554
1555 f_init();
1556
1557 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001558 if(ep_nr > 15) {
1559 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1560 } else {
1561 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1562 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001563 call_id := int2hex(ep_nr, 2) & '1234'H;
1564 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1565 }
1566 }
1567
Harald Welte79181ff2017-11-18 19:26:11 +01001568 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1569 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001570 var template MgcpCommand cmd;
1571 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001572 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001573 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001574
Harald Welteedc45c12017-11-18 19:15:05 +01001575 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001576
Harald Welteba62c8c2017-11-18 18:26:49 +01001577 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001578 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001579
Harald Welteba62c8c2017-11-18 18:26:49 +01001580 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001581
1582 setverdict(pass);
1583 }
1584
Harald Welte79181ff2017-11-18 19:26:11 +01001585 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1586 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1587 var template MgcpCommand cmd;
1588 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001589 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001590 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001591
1592 f_init(ep);
1593
1594 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1595 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1596
1597 f_dlcx_ok(ep);
1598
1599 setverdict(pass);
1600 }
1601
1602
Harald Welte6d167f82017-11-18 19:41:35 +01001603 /* CRCX + DLCX of valid endpoint but invalid call-id */
1604 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1605 var template MgcpCommand cmd;
1606 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001607 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001608 var MgcpCallId call_id := '51231'H;
1609
1610 f_init(ep);
1611
1612 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1613 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1614
1615 f_dlcx(ep, "516", *, 'ffff'H);
1616
1617 setverdict(pass);
1618 }
1619
1620
1621 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1622 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1623 var template MgcpCommand cmd;
1624 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001625 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001626 var MgcpCallId call_id := '51230'H;
1627
1628 f_init(ep);
1629
1630 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1631 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1632
1633 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1634
1635 setverdict(pass);
1636 }
1637
1638
Harald Weltee636afd2017-09-17 16:24:09 +08001639 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001640 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1641 var template MgcpCommand cmd;
1642 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001643 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001644 var MgcpCallId call_id := '51229'H;
1645 var template MgcpResponse rtmpl := {
1646 line := {
1647 code := "200",
1648 string := "OK"
1649 },
1650 params:= { },
1651 sdp := omit
1652 };
1653
1654 f_init(ep);
1655
1656 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1657 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1658
1659 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1660 resp := mgcp_transceive_mgw(cmd, rtmpl);
1661 resp := mgcp_transceive_mgw(cmd, rtmpl);
1662
1663 setverdict(pass);
1664 }
1665
Harald Weltebb7523b2018-03-29 08:52:01 +02001666 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1667 charstring codec) := {
1668 em := {
1669 hostname := host_a,
1670 portnr := omit
1671 },
1672 mgw := {
1673 hostname := host_b,
1674 portnr := omit
1675 },
1676 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001677 codec := codec,
1678 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001679 }
Harald Weltef53f1642017-11-18 19:57:11 +01001680
Harald Weltebb7523b2018-03-29 08:52:01 +02001681 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1682 testcase TC_rtpem_selftest() runs on dummy_CT {
1683 var RtpemStats stats[2];
1684 var integer local_port := 10000;
1685 var integer local_port2 := 20000;
1686
1687 f_init();
1688
1689 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1690 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1691
1692 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1693 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1694
1695 log("=== starting");
1696 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1697 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1698
1699 f_sleep(5.0);
1700
1701 log("=== stopping");
1702 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1703 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1704 f_sleep(0.5);
1705 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1706 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1707
1708 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1709 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1710 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1711 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001712 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001713 }
1714 setverdict(pass);
1715 }
1716
Philipp Maier2321ef92018-06-27 17:52:04 +02001717 /* Create one half open connection in receive-only mode. The MGW must accept
1718 * the packets but must not send any. */
1719 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1720 var RtpFlowData flow;
1721 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1722 var MgcpCallId call_id := '1225'H;
1723 var RtpemStats stats;
1724
1725 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001726 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001727 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001728 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001729
1730 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1731 f_sleep(1.0);
1732 f_flow_delete(RTPEM[0], ep, call_id);
1733
1734 stats := f_rtpem_stats_get(RTPEM[0]);
1735
Philipp Maier42b17cc2019-10-01 13:53:17 +02001736 /* Make sure that at least some amount of RTP packets/bytes
1737 * have has been transmitted. The compare values for
1738 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1739 * using a testrun and the results were devided by 2, so even
1740 * in load situations we should reach the minimum amount of
1741 * required packets/bytes */
1742
1743 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001744 setverdict(fail);
1745 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001746 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001747 setverdict(fail);
1748 }
Philipp Maier36291392018-07-25 09:40:44 +02001749
1750 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001751
1752 setverdict(pass);
1753 }
1754
1755 /* Create one connection in loopback mode, test if the RTP packets are
1756 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001757 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 +02001758 var RtpFlowData flow;
1759 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1760 var MgcpCallId call_id := '1225'H;
1761 var RtpemStats stats;
1762
1763 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001764 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001765 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001766 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001767
1768 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1769 f_sleep(1.0);
1770 f_flow_delete(RTPEM[0], ep, call_id);
1771
1772 stats := f_rtpem_stats_get(RTPEM[0]);
1773
1774 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1775 setverdict(fail);
1776 }
1777 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1778 setverdict(fail);
1779 }
Philipp Maier36291392018-07-25 09:40:44 +02001780
1781 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001782
1783 setverdict(pass);
1784 }
1785
Philipp Maier1ac13982021-05-07 23:06:07 +02001786 /* Create one connection in loopback mode, test if the RTP packets are
1787 * actually reflected */
1788 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001789 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001790 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001791 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1792 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1793 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001794
1795 /* Same as above, but we will intenionally not tell the MGW where to
1796 * send the outgoing traffic. The connection is still created in
1797 * loopback mode, so the MGW should take the originating address from
1798 * the incoming RTP packet and send it back to the source */
1799 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001800 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001801 }
1802
1803
Philipp Maier7df85f62018-07-25 10:26:09 +02001804 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1805 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001806 var RtpFlowData flow[2];
1807 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001808 var MgcpResponse resp;
1809 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1810 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001811 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001812
1813 f_init(ep);
1814
1815 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001816 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001817 /* bind local RTP emulation sockets */
1818 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001819 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001820
1821 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001822 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001823 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001824 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1825
1826 if (bidir) {
1827 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1828 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1829
1830 /* Note: When we test bidirectional we may
1831 * loose packets during switch off because
1832 * both ends are transmitting and we only
1833 * can switch them off one by one. */
1834 tolerance := 3;
1835 } else {
1836 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1837 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1838 }
1839
1840 f_sleep(1.0);
1841
1842 f_flow_delete(RTPEM[1]);
1843 f_flow_delete(RTPEM[0], ep, call_id);
1844
1845 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1846 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1847 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1848 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001849 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001850 }
1851
Philipp Maier36291392018-07-25 09:40:44 +02001852 f_rtpem_stats_err_check(stats[0]);
1853 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001854
Philipp Maier2321ef92018-06-27 17:52:04 +02001855 setverdict(pass);
1856 }
1857
1858 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1859 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001860 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001861 }
1862
1863 /* create two local RTP emulations; create two connections on MGW EP,
1864 * exchange some data in both directions */
1865 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001866 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1867 }
1868
1869 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1870 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1871 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
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_bidir() runs on dummy_CT {
1876 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001877 }
1878
1879 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001880 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1881 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001882 var RtpFlowData flow[2];
1883 var RtpemStats stats[2];
1884 var MgcpResponse resp;
1885 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1886 var MgcpCallId call_id := '1227'H;
1887 var integer num_pkts_tx[2];
1888 var integer temp;
1889
1890 f_init(ep);
1891
1892 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001893 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001894 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001895 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001896
1897 /* Create the second connection. This connection will be also
1898 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001899 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001900 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001901 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001902
1903 /* The first leg starts transmitting */
1904 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1905 f_sleep(0.5);
1906 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1907 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001908 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001909 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001910 }
1911 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1912 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001913 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001914 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001915 }
1916
1917 /* The second leg starts transmitting a little later */
1918 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1919 f_sleep(1.0);
1920 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1921 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001922 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001923 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001924 }
1925 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1926 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001927 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001928 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001929 }
1930
1931 /* The first leg will now be switched into bidirectional
1932 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001933 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1934 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1935 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001936 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1937 f_sleep(0.5);
1938 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001939 if (stats[0].num_pkts_rx_err_disabled != 0) {
1940 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001941 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001942 }
1943 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1944 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001945 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001946 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001947 }
1948
1949 /* When the second leg is switched into bidirectional mode
1950 * as well, then the MGW will connect the two together and
1951 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02001952 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1953 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001954 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001955 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1956 f_sleep(2.0);
1957
1958 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1959 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1960
1961 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1962 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001963 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001964 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001965 }
1966
1967 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1968 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001969 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001970 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001971 }
Philipp Maier36291392018-07-25 09:40:44 +02001972
1973 f_rtpem_stats_err_check(stats[0]);
1974 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001975
1976 /* Tear down */
1977 f_flow_delete(RTPEM[0]);
1978 f_flow_delete(RTPEM[1], ep, call_id);
1979 setverdict(pass);
1980 }
1981
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001982 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1983 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1984 mp_local_ipv4, mp_remote_ipv4);
1985 }
1986
1987 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
1988 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
1989 mp_local_ipv6, mp_remote_ipv6);
1990 }
1991
1992 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
1993 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1994 mp_local_ipv6, mp_remote_ipv6);
1995 }
1996
Philipp Maier2321ef92018-06-27 17:52:04 +02001997 /* Test what happens when two RTP streams from different sources target
1998 * a single connection. Is the unsolicited stream properly ignored? */
1999 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2000 var RtpFlowData flow[2];
2001 var RtpemStats stats[2];
2002 var MgcpResponse resp;
2003 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2004 var MgcpCallId call_id := '1234321326'H;
2005 var integer unsolicited_port := 10002;
2006
2007 f_init(ep);
2008
2009 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002010 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002011 /* bind local RTP emulation sockets */
2012 flow[0].em.portnr := 10000;
2013 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2014
2015 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002016 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002017 flow[1].em.portnr := 20000;
2018 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002019
2020 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2021 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2022
Philipp Maier2321ef92018-06-27 17:52:04 +02002023 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002024
Philipp Maier2321ef92018-06-27 17:52:04 +02002025 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002026 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2027 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002028 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2029
2030 f_sleep(0.5);
2031
Daniel Willmanna069d382018-12-13 13:53:33 +01002032 /* Stop transmitting packets and tear down the flows */
2033 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002034 f_flow_delete(RTPEM[0]);
2035 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002036
2037 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2038 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2039 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2040 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002041 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002042 }
2043
Philipp Maier36291392018-07-25 09:40:44 +02002044 f_rtpem_stats_err_check(stats[0]);
2045 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002046
Harald Weltebb7523b2018-03-29 08:52:01 +02002047 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002048 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002049
Philipp Maier2321ef92018-06-27 17:52:04 +02002050 /* Test a handover situation. We first create two connections transmit
2051 * some data bidirectionally. Then we will simulate a handover situation. */
2052 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2053 var RtpFlowData flow[2];
2054 var RtpemStats stats[3];
2055 var MgcpResponse resp;
2056 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2057 var MgcpCallId call_id := '76338'H;
2058 var integer port_old;
2059
2060 f_init(ep);
2061
2062 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002063 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002064 /* bind local RTP emulation sockets */
2065 flow[0].em.portnr := 10000;
2066 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2067
2068 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002069 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002070 flow[1].em.portnr := 20000;
2071 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2072
2073 /* Normal rtp flow for one second */
2074 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2075 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2076 f_sleep(1.0);
2077
2078 /* Now switch the flow over to a new port (BTS) */
2079 port_old := flow[0].em.portnr;
2080 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002081 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002082 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002083 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002084
2085 /* When handing over a call, the old source may still keep
2086 * transmitting for a while. We simulate this by injecting
2087 * some unsolicited packets on the behalf of the old source,
2088 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002089 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2090 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002091 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2092 f_sleep(1.0);
2093 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2094 f_sleep(1.0);
2095
2096 /* Terminate call */
2097 f_flow_delete(RTPEM[0]);
2098 f_flow_delete(RTPEM[1], ep, call_id);
2099
2100 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2101 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2102 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2103 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002104 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002105 }
2106 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2107 if (stats[2].num_pkts_rx_err_disabled != 0) {
2108 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002109 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002110 }
2111
Philipp Maier36291392018-07-25 09:40:44 +02002112 f_rtpem_stats_err_check(stats[0]);
2113 f_rtpem_stats_err_check(stats[1]);
2114 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002115
Philipp Maier2321ef92018-06-27 17:52:04 +02002116 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002117 }
Harald Weltef53f1642017-11-18 19:57:11 +01002118
Philipp Maier6d4e0942019-02-21 17:35:01 +01002119
2120 /* create two local RTP emulations; create two connections on MGW EP, see if
2121 * exchanged data is converted bwtween ts101318 and rfc5993 */
2122 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2123 var RtpFlowData flow[2];
2124 var RtpemStats stats[2];
2125 var MgcpResponse resp;
2126 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2127 var MgcpCallId call_id := '1226'H;
2128
2129 f_init(ep);
2130
2131 /* Turn on conversion mode */
2132 f_vty_enter_config(MGWVTY);
2133 f_vty_transceive(MGWVTY, "mgcp");
2134 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2135
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002136 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002137 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002138 /* bind local RTP emulation sockets */
2139 flow[0].em.portnr := 10000;
2140 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2141 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2142 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2143 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2144 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2145
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002146 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002147 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002148 flow[1].em.portnr := 20000;
2149 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2150 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2151 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2152 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2153 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2154
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002155 /* Send RTP packets to connection #0, receive on connection #1 */
2156 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2157 f_sleep(0.5);
2158 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002159 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002160 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2161 f_sleep(0.5);
2162 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002163
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002164 /* Send RTP packets to connection #1, receive on connection #0 */
2165 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2166 f_sleep(0.5);
2167 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2168 f_sleep(1.0);
2169 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2170 f_sleep(0.5);
2171 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2172
2173 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002174 f_flow_delete(RTPEM[0]);
2175 f_flow_delete(RTPEM[1], ep, call_id);
2176
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002177 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002178 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2179 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002180 f_rtpem_stats_err_check(stats[0]);
2181 f_rtpem_stats_err_check(stats[1]);
2182
2183 /* Turn off conversion mode */
2184 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2185
2186 setverdict(pass);
2187 }
2188
Philipp Maier4f764ce2019-03-07 10:54:10 +01002189 /* create two local RTP emulations; create two connections on MGW EP, see if
2190 * exchanged data is converted between AMR octet-aligned and bandwith
2191 * efficient-mode */
2192 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2193 var RtpFlowData flow[2];
2194 var RtpemStats stats[2];
2195 var MgcpResponse resp;
2196 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2197 var MgcpCallId call_id := '1226'H;
2198
2199 f_init(ep);
2200
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002201 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002202 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002203 /* bind local RTP emulation sockets */
2204 flow[0].em.portnr := 10000;
2205 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2206 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2207 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2208 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2209 flow[0].fmtp := fmtp0;
2210 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2211
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002212 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002213 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002214 flow[1].em.portnr := 20000;
2215 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2216 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2217 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2218 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2219 flow[1].fmtp := fmtp1;
2220 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2221
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002222 /* Send RTP packets to connection #0, receive on connection #1 */
2223 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2224 f_sleep(0.5);
2225 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002226 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002227 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2228 f_sleep(0.5);
2229 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002230
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002231 /* Send RTP packets to connection #1, receive on connection #0 */
2232 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2233 f_sleep(0.5);
2234 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2235 f_sleep(1.0);
2236 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2237 f_sleep(0.5);
2238 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2239
2240 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002241 f_flow_delete(RTPEM[0]);
2242 f_flow_delete(RTPEM[1], ep, call_id);
2243
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002244 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002245 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2246 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002247 f_rtpem_stats_err_check(stats[0]);
2248 f_rtpem_stats_err_check(stats[1]);
2249
2250 setverdict(pass);
2251 }
2252
Philipp Maier882843d2020-05-25 15:33:13 +02002253 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2254 * functions are real world AMR RTP payloads including AMR header. The
2255 * payloads were extracted from a trace with known good payloads. */
2256
Philipp Maier4f764ce2019-03-07 10:54:10 +01002257 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002258 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002259 }
2260
2261 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2262 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2263 }
2264
2265 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2266 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2267 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002268
Harald Weltee636afd2017-09-17 16:24:09 +08002269 /* TODO: Double-DLCX (no retransmission) */
2270
2271
2272
2273 /* TODO: AUEP (various) */
2274 /* TODO: RSIP (various) */
2275 /* TODO: RQNT (various) */
2276 /* TODO: EPCF (various) */
2277 /* TODO: AUCX (various) */
2278 /* TODO: invalid verb (various) */
2279
Oliver Smith021141e2019-06-25 12:09:01 +02002280
2281 testcase TC_conn_timeout() runs on dummy_CT {
2282 var RtpFlowData flow;
2283 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2284 var MgcpCallId call_id := '1225'H;
2285 var MGCP_RecvFrom mrf;
2286
2287 f_init(ep);
2288 log("Setting conn-timeout to 1s");
2289 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2290
2291 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002292 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002293 flow.em.portnr := 10000;
2294 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2295 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2296 f_sleep(1.5);
2297
2298 log("Stopping for 0.5s and resuming");
2299 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2300 f_sleep(0.5);
2301 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2302 f_sleep(0.1);
2303
2304 log("Stopping for 1.5s, expecting to run into timeout");
2305 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2306 f_sleep(1.5);
2307
2308 log("Resuming should fail now");
2309 f_rtpem_conn_refuse_expect(RTPEM[0]);
2310 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2311 f_sleep(0.2);
2312 f_rtpem_conn_refuse_verify(RTPEM[0]);
2313
2314 setverdict(pass);
2315 }
2316
Philipp Maier2609c752020-07-08 12:38:09 +02002317 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2318 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2319 var template MgcpCommand cmd;
2320 var MgcpResponse resp;
2321 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2322 var MgcpCallId call_id := '8376F297'H;
2323
2324 f_init(ep);
2325
2326 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2327 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2328
2329 f_dlcx_ok(ep);
2330
2331 setverdict(pass);
2332 }
2333
2334 /* Test what happens when overlapping endpoints are selected (E1) */
2335 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2336 var template MgcpCommand cmd;
2337 var MgcpResponse resp;
2338 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2339 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2340 var MgcpCallId call_id_1 := '8376F297'H;
2341 var MgcpCallId call_id_2 := '837AF2A7'H;
2342
2343 f_init();
2344
2345 /* ep_1 and ep_2 are overlapping, selecting both one after
2346 * another should work fine: */
2347 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2348 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2349 f_dlcx_ok(ep_1);
2350 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2351 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2352 f_dlcx_ok(ep_2);
2353
2354 /* When ep_1 is serving a call we can not select ep_2 becaus
2355 * it is overlapping with ep_1 */
2356 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2357 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2358 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2359 resp := mgcp_transceive_mgw(cmd, ?);
2360 if (resp.line.code != "501") {
2361 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2362 }
2363 f_dlcx_ok(ep_1);
2364
2365 setverdict(pass);
2366 }
2367
2368 /* Create one connection in loopback mode, test if the RTP packets are
2369 * actually reflected */
2370 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2371 var RtpFlowData flow;
2372 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2373 var MgcpCallId call_id := '12250989'H;
2374 var RtpemStats stats;
2375
2376 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002377 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002378 flow.em.portnr := 10000;
2379 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2380
2381 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2382 f_sleep(1.0);
2383 f_flow_delete(RTPEM[0], ep, call_id);
2384
2385 stats := f_rtpem_stats_get(RTPEM[0]);
2386
2387 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2388 setverdict(fail);
2389 }
2390 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2391 setverdict(fail);
2392 }
2393
2394 f_rtpem_stats_err_check(stats);
2395
2396 setverdict(pass);
2397 }
2398
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002399 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2400 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2401 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2402 var template MgcpCommand cmd;
2403 var MgcpResponse resp;
2404 var MgcpCallId call_id := '1234'H;
2405 var MgcpConnectionId conn_id;
2406
2407 f_init(ep);
2408
2409 /* create the connection on the MGW */
2410 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2411 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2412 conn_id := extract_conn_id(resp);
2413
2414 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2415 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2416 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2417 valueof(ts_SDP_ptime(20)) });
2418 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2419
2420 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2421 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2422 }
2423 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2424 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2425 }
2426
2427 /* clean-up */
2428 f_dlcx_ok(ep, call_id);
2429 setverdict(pass);
2430 }
2431
2432 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2433 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2434 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2435 var template MgcpCommand cmd;
2436 var MgcpResponse resp;
2437 var MgcpCallId call_id := '1234'H;
2438 var MgcpConnectionId conn_id;
2439
2440 f_init(ep);
2441
2442 /* create the connection on the MGW */
2443 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2444 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2445 conn_id := extract_conn_id(resp);
2446
2447 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2448 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2449 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2450 valueof(ts_SDP_ptime(20)) });
2451 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2452
2453 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2454 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2455 }
2456 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2457 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2458 }
2459
2460 /* clean-up */
2461 f_dlcx_ok(ep, call_id);
2462 setverdict(pass);
2463 }
2464
Harald Welte00a067f2017-09-13 23:27:17 +02002465 control {
2466 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002467 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002468 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002469 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002470 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002471 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002472 execute(TC_crcx_early_bidir_mode());
2473 execute(TC_crcx_unsupp_param());
2474 execute(TC_crcx_missing_callid());
2475 execute(TC_crcx_missing_mode());
2476 execute(TC_crcx_unsupp_packet_intv());
2477 execute(TC_crcx_illegal_double_lco());
2478 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002479 execute(TC_crcx_wildcarded());
2480 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002481 execute(TC_mdcx_without_crcx());
2482 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02002483 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002484 execute(TC_mdcx_wildcarded());
2485 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002486 execute(TC_crcx_and_dlcx_ep_callid_connid());
2487 execute(TC_crcx_and_dlcx_ep_callid());
2488 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002489 execute(TC_crcx_and_dlcx_ep_callid_inval());
2490 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002491 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002492
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002493 execute(TC_crcx_osmux_wildcard());
2494 execute(TC_crcx_osmux_fixed());
2495 execute(TC_crcx_osmux_fixed_twice());
2496 execute(TC_one_crcx_receive_only_osmux());
2497 execute(TC_one_crcx_loopback_osmux());
2498 execute(TC_two_crcx_and_rtp_osmux());
2499 execute(TC_two_crcx_and_rtp_osmux_bidir());
2500 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2501 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2502
Harald Welte33d82162017-12-28 03:21:57 +01002503 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002504
2505 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002506
2507 execute(TC_one_crcx_receive_only_rtp());
2508 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002509 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02002510 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002511 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002512 execute(TC_two_crcx_diff_pt_and_rtp());
2513 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002514 execute(TC_two_crcx_mdcx_and_rtp());
2515 execute(TC_two_crcx_and_unsolicited_rtp());
2516 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002517 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002518 execute(TC_amr_oa_bwe_rtp_conversion());
2519 execute(TC_amr_oa_oa_rtp_conversion());
2520 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002521
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002522 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002523
2524 execute(TC_e1_crcx_and_dlcx_ep());
2525 execute(TC_e1_crcx_with_overlap());
2526 execute(TC_e1_crcx_loopback());
2527
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002528 execute(TC_crcx_mdcx_ip4());
2529 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002530 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2531 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2532 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2533 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2534 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2535 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2536 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2537 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02002538
2539 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
2540 * older versions of osmo-mgw. This eventually leads into
2541 * a failure of all subsequent testcases, so it is important
2542 * not to add new testcaes after this one. */
2543 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02002544 }
2545}