blob: 699067970bc58d30158a15b29c30a8873c7bb6c4 [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 Maier8a3dc922018-02-02 14:55:12 +01001029 /* test valid wildcarded MDCX */
1030 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1031 /* Note: A wildcarded MDCX is not allowed, so we expect the
1032 * MGW to reject this request */
1033 var template MgcpCommand cmd;
1034 var MgcpResponse resp;
1035 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1036 var MgcpCallId call_id := '1225'H;
1037 var template MgcpResponse rtmpl := {
1038 line := {
1039 /* TODO: accept/enforce better error? */
1040 code := "507",
1041 string := ?
1042 },
1043 params:= { },
1044 sdp := omit
1045 };
1046
1047 f_init(ep);
1048
1049 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1050 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1051 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1052 valueof(ts_SDP_ptime(20)) });
1053 resp := mgcp_transceive_mgw(cmd, rtmpl);
1054 setverdict(pass);
1055 }
1056
1057 /* test valid wildcarded DLCX */
1058 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001059 var template MgcpCommand cmd;
1060 var MgcpResponse resp;
1061 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001062 const integer n_endpoints := 31;
1063 var integer i;
1064 var MgcpCallId call_id := '1234'H;
1065 var StatsDExpects expect;
1066 f_init(ep);
1067
1068 /* Allocate a few endpoints */
1069 for (i := 0; i < n_endpoints; i := i+1) {
1070 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1071 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1072 }
1073
1074 expect := {
1075 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := n_endpoints, max := n_endpoints}
1076 };
1077 f_statsd_expect(expect);
1078
1079 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001080 var template MgcpResponse rtmpl := {
1081 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001082 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001083 string := ?
1084 },
1085 params:= { },
1086 sdp := omit
1087 };
Philipp Maier55b90542021-07-02 12:33:19 +02001088 cmd := ts_DLCX(get_next_trans_id(), ep);
1089 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001090
Philipp Maier55b90542021-07-02 12:33:19 +02001091 /* The stats reporter collects multiple samples during the reporting interval and
1092 * reports the highest back to the user. This means we will not immediately get
1093 * the 0 endpoints but an intermediate result instead. */
1094 expect := {
1095 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := n_endpoints}
1096 };
1097 f_statsd_expect(expect);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001098
Philipp Maier55b90542021-07-02 12:33:19 +02001099 /* The second interval must resturn a result with 0 endpoints in use. */
1100 expect := {
1101 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1102 };
1103 f_statsd_expect(expect);
1104
Philipp Maier8a3dc922018-02-02 14:55:12 +01001105 setverdict(pass);
1106 }
1107
Harald Welte79181ff2017-11-18 19:26:11 +01001108 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1109 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1110 var template MgcpCommand cmd;
1111 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001112 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001113 var MgcpCallId call_id := '51234'H;
1114
1115 f_init(ep);
1116
1117 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1118 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1119
1120 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1121
1122 setverdict(pass);
1123 }
1124
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001125 /* test valid CRCX without SDP */
1126 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1127 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1128 setverdict(pass);
1129 }
1130
1131 /* test valid CRCX without SDP */
1132 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1133 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1134 setverdict(pass);
1135 }
1136
1137 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1138 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1139 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1140 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1141 setverdict(pass);
1142 }
1143
1144 /* Create one half open connection in receive-only mode. The MGW must accept
1145 * the packets but must not send any. */
1146 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1147 var RtpFlowData flow;
1148 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1149 var MgcpCallId call_id := '1225'H;
1150 var OsmuxemStats stats;
1151 var OsmuxTxHandle tx_hdl;
1152
1153 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001154 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001155 flow.em.portnr := mp_local_osmux_port;
1156 flow.osmux_cid := -1;
1157 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1158
1159 /* create a transmitter not yet known by MGW */
1160 tx_hdl := valueof(t_TxHandleAMR590(2));
1161 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1162
1163 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1164 f_sleep(1.0);
1165 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1166
1167 stats := f_osmuxem_stats_get(OsmuxEM);
1168
1169 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1170 setverdict(fail);
1171 }
1172 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1173 setverdict(fail);
1174 }
1175
1176 f_osmuxem_stats_err_check(stats);
1177
1178 setverdict(pass);
1179 }
1180
1181 /* Create one connection in loopback mode, test if the Osmux packets are
1182 * actually reflected */
1183 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1184 var RtpFlowData flow;
1185 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1186 var MgcpCallId call_id := '1225'H;
1187 var OsmuxemStats stats;
1188 var OsmuxTxHandle tx_hdl;
1189
1190 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001191 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001192 flow.em.portnr := mp_local_osmux_port;
1193 flow.osmux_cid := 2;
1194 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1195
1196 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1197 f_sleep(1.0);
1198
1199 /* Switch off both Tx, wait to receive delayed frames from MGW */
1200 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1201 f_sleep(0.1);
1202 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1203
1204 stats := f_osmuxem_stats_get(OsmuxEM);
1205
1206 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1207 setverdict(fail);
1208 }
1209 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1210 setverdict(fail);
1211 }
1212
1213 f_osmuxem_stats_err_check(stats);
1214
1215 setverdict(pass);
1216 }
1217
1218 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1219 * must match the reception statistics on the other side and vice versa. The
1220 * user may also supply a tolerance value (number of packets) when deviations
1221 * are acceptable */
1222 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1223 var integer plen;
1224
1225 log("stats A: ", a);
1226 log("stats B: ", b);
1227 log("tolerance: ", tolerance, " packets");
1228 log("batch_size: ", batch_size, " packets");
1229
1230 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1231
1232 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1233 return false;
1234 }
1235
1236 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1237 return false;
1238 }
1239
1240 if(a.num_pkts_tx > 0) {
1241 plen := a.bytes_payload_tx / a.num_pkts_tx;
1242 } else {
1243 plen := 0;
1244 }
1245
1246 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1247 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1248 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1249 return false;
1250 }
1251
1252 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) {
1253 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1254 return false;
1255 }
1256
1257 return true;
1258 }
1259
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001260 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1261 charstring local_ip_rtp, charstring remote_ip_rtp,
1262 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001263 var RtpFlowData flow[2];
1264 var RtpemStats stats_rtp;
1265 var OsmuxemStats stats_osmux;
1266 var MgcpResponse resp;
1267 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1268 var MgcpCallId call_id := '1226'H;
1269 var integer tolerance := 0;
1270
1271 f_init(ep, true);
1272
1273 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001274 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001275 flow[0].rtp_cfg := c_RtpemDefaultCfg
1276 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1277 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1278 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);
1279 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1280 /* bind local RTP emulation sockets */
1281 flow[0].em.portnr := 10000;
1282 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1283
1284 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001285 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001286 flow[1].em.portnr := mp_local_osmux_port;
1287 flow[1].osmux_cid := 2;
1288 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1289 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1290
1291 if (bidir) {
1292 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1293 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1294
1295 /* Note: When we test bidirectional we may
1296 * loose packets during switch off because
1297 * both ends are transmitting and we only
1298 * can switch them off one by one. */
1299 tolerance := 3;
1300 } else {
1301 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1302 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1303 }
1304
1305 f_sleep(1.0);
1306
1307 /* Switch off both Tx, wait to receive delayed frames from MGW */
1308 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1309 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1310 f_sleep(0.1);
1311
1312 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1313 f_flow_delete(RTPEM[1]);
1314
1315 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1316 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1317 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1318 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1319 mtc.stop;
1320 }
1321
1322 f_rtpem_stats_err_check(stats_rtp);
1323 f_osmuxem_stats_err_check(stats_osmux);
1324
1325 setverdict(pass);
1326 }
1327
1328 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1329 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001330 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1331 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001332 }
1333
1334 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1335 * exchange some data in both directions */
1336 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001337 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1338 mp_local_ipv4, mp_remote_ipv4);
1339 }
1340
1341 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1342 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1343 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1344 mp_local_ipv6, mp_remote_ipv6);
1345 }
1346 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1347 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1348 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1349 mp_local_ipv6, mp_remote_ipv6);
1350 }
1351 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1352 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1353 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1354 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001355 }
1356
1357
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001358 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1359 charstring local_ip_rtp, charstring remote_ip_rtp,
1360 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001361 var RtpFlowData flow[2];
1362 var RtpemStats stats_rtp;
1363 var OsmuxemStats stats_osmux;
1364 var MgcpResponse resp;
1365 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1366 var MgcpCallId call_id := '1227'H;
1367 var integer num_pkts_tx[2];
1368 var integer temp;
1369
1370 f_init(ep, true);
1371
1372 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001373 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001374 flow[0].rtp_cfg := c_RtpemDefaultCfg
1375 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1376 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1377 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);
1378 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1379 /* bind local RTP emulation sockets */
1380 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001381 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001382
1383
1384 /* Create the second connection. This connection will be also
1385 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001386 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001387 flow[1].em.portnr := mp_local_osmux_port;
1388 if (crcx_osmux_wildcard) {
1389 flow[1].osmux_cid := -1;
1390 } else {
1391 flow[1].osmux_cid := 2;
1392 }
1393 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001394 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001395
1396
1397 /* The first leg starts transmitting */
1398 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1399 f_sleep(0.5);
1400 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1401 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1402 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1403 mtc.stop;
1404 }
1405 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1406 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1407 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1408 mtc.stop;
1409 }
1410
1411 /* The second leg starts transmitting a little later */
1412 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1413 f_sleep(1.0);
1414 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1415 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1416 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1417 mtc.stop;
1418 }
1419 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1420 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1421 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1422 mtc.stop;
1423 }
1424
1425 /* The first leg will now be switched into bidirectional
1426 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001427 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1428 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1429 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001430 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1431 f_sleep(0.5);
1432 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1433 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1434 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1435 mtc.stop;
1436 }
1437 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1438 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1439 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1440 mtc.stop;
1441 }
1442
1443 /* When the second leg is switched into bidirectional mode
1444 * as well, then the MGW will connect the two together and
1445 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001446 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1447 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001448 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001449
1450 if (crcx_osmux_wildcard) {
1451 /* For now we must set same CID as the MGW recvCID,
1452 * having sendCID!=recvCID is not yet supported. */
1453 flow[1].osmux_cid := flow[1].osmux_cid_response;
1454 }
1455 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1456 f_sleep(2.0);
1457
1458 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1459 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1460
1461 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1462 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1463 log("stats_rtp: ", stats_rtp);
1464 log("stats_osmux: ", stats_osmux);
1465 log("old_rtp_tx: ", num_pkts_tx[0]);
1466 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1467 mtc.stop;
1468 }
1469
1470 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1471 if (temp > 3 or temp < -3) {
1472 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1473 mtc.stop;
1474 }
1475
1476 f_rtpem_stats_err_check(stats_rtp);
1477 f_osmuxem_stats_err_check(stats_osmux);
1478
1479 /* Tear down */
1480 f_flow_delete(RTPEM[0]);
1481 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1482 setverdict(pass);
1483 }
1484
1485 /* create one RTP and one OSmux emulations and pass data in both
1486 directions. Create CRCX with wildcard Osmux CID and set it later
1487 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1488 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001489 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1490 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001491 }
1492
1493 /* create one RTP and one OSmux emulations and pass data in both
1494 directions. Create CRCX with fixed Osmux CID and keep it during
1495 MDCX. This is similar to how BSC sets up the call in AoIP. */
1496 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001497 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1498 mp_local_ipv4, mp_remote_ipv4);
1499 }
1500
1501 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1502 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1503 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1504 mp_local_ipv6, mp_remote_ipv6);
1505 }
1506 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1507 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1508 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1509 mp_local_ipv6, mp_remote_ipv6);
1510 }
1511 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1512 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1513 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1514 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001515 }
1516
Harald Welte646ecdb2017-12-28 03:21:57 +01001517 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1518 var template MgcpCommand cmd;
1519 var MgcpResponse resp;
1520
1521 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1522 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1523
1524 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1525
1526 setverdict(pass);
1527 }
1528
1529 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1530 var MgcpEndpoint ep;
1531 var MgcpCallId call_id;
1532 var integer ep_nr;
1533
1534 f_init();
1535
1536 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001537 if(ep_nr > 15) {
1538 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1539 } else {
1540 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1541 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001542 call_id := int2hex(ep_nr, 2) & '1234'H;
1543 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1544 }
1545 }
1546
Harald Welte79181ff2017-11-18 19:26:11 +01001547 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1548 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001549 var template MgcpCommand cmd;
1550 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001551 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001552 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001553
Harald Welteedc45c12017-11-18 19:15:05 +01001554 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001555
Harald Welteba62c8c2017-11-18 18:26:49 +01001556 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001557 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001558
Harald Welteba62c8c2017-11-18 18:26:49 +01001559 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001560
1561 setverdict(pass);
1562 }
1563
Harald Welte79181ff2017-11-18 19:26:11 +01001564 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1565 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1566 var template MgcpCommand cmd;
1567 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001568 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001569 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001570
1571 f_init(ep);
1572
1573 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1574 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1575
1576 f_dlcx_ok(ep);
1577
1578 setverdict(pass);
1579 }
1580
1581
Harald Welte6d167f82017-11-18 19:41:35 +01001582 /* CRCX + DLCX of valid endpoint but invalid call-id */
1583 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1584 var template MgcpCommand cmd;
1585 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001586 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001587 var MgcpCallId call_id := '51231'H;
1588
1589 f_init(ep);
1590
1591 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1592 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1593
1594 f_dlcx(ep, "516", *, 'ffff'H);
1595
1596 setverdict(pass);
1597 }
1598
1599
1600 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1601 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1602 var template MgcpCommand cmd;
1603 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001604 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001605 var MgcpCallId call_id := '51230'H;
1606
1607 f_init(ep);
1608
1609 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1610 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1611
1612 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1613
1614 setverdict(pass);
1615 }
1616
1617
Harald Weltee636afd2017-09-17 16:24:09 +08001618 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001619 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1620 var template MgcpCommand cmd;
1621 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001622 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001623 var MgcpCallId call_id := '51229'H;
1624 var template MgcpResponse rtmpl := {
1625 line := {
1626 code := "200",
1627 string := "OK"
1628 },
1629 params:= { },
1630 sdp := omit
1631 };
1632
1633 f_init(ep);
1634
1635 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1636 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1637
1638 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1639 resp := mgcp_transceive_mgw(cmd, rtmpl);
1640 resp := mgcp_transceive_mgw(cmd, rtmpl);
1641
1642 setverdict(pass);
1643 }
1644
Harald Weltebb7523b2018-03-29 08:52:01 +02001645 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1646 charstring codec) := {
1647 em := {
1648 hostname := host_a,
1649 portnr := omit
1650 },
1651 mgw := {
1652 hostname := host_b,
1653 portnr := omit
1654 },
1655 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001656 codec := codec,
1657 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001658 }
Harald Weltef53f1642017-11-18 19:57:11 +01001659
Harald Weltebb7523b2018-03-29 08:52:01 +02001660 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1661 testcase TC_rtpem_selftest() runs on dummy_CT {
1662 var RtpemStats stats[2];
1663 var integer local_port := 10000;
1664 var integer local_port2 := 20000;
1665
1666 f_init();
1667
1668 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1669 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1670
1671 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1672 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1673
1674 log("=== starting");
1675 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1676 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1677
1678 f_sleep(5.0);
1679
1680 log("=== stopping");
1681 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1682 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1683 f_sleep(0.5);
1684 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1685 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1686
1687 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1688 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1689 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1690 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001691 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001692 }
1693 setverdict(pass);
1694 }
1695
Philipp Maier2321ef92018-06-27 17:52:04 +02001696 /* Create one half open connection in receive-only mode. The MGW must accept
1697 * the packets but must not send any. */
1698 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1699 var RtpFlowData flow;
1700 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1701 var MgcpCallId call_id := '1225'H;
1702 var RtpemStats stats;
1703
1704 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001705 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001706 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001707 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001708
1709 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1710 f_sleep(1.0);
1711 f_flow_delete(RTPEM[0], ep, call_id);
1712
1713 stats := f_rtpem_stats_get(RTPEM[0]);
1714
Philipp Maier42b17cc2019-10-01 13:53:17 +02001715 /* Make sure that at least some amount of RTP packets/bytes
1716 * have has been transmitted. The compare values for
1717 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1718 * using a testrun and the results were devided by 2, so even
1719 * in load situations we should reach the minimum amount of
1720 * required packets/bytes */
1721
1722 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001723 setverdict(fail);
1724 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001725 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001726 setverdict(fail);
1727 }
Philipp Maier36291392018-07-25 09:40:44 +02001728
1729 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001730
1731 setverdict(pass);
1732 }
1733
1734 /* Create one connection in loopback mode, test if the RTP packets are
1735 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001736 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 +02001737 var RtpFlowData flow;
1738 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1739 var MgcpCallId call_id := '1225'H;
1740 var RtpemStats stats;
1741
1742 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001743 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001744 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001745 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001746
1747 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1748 f_sleep(1.0);
1749 f_flow_delete(RTPEM[0], ep, call_id);
1750
1751 stats := f_rtpem_stats_get(RTPEM[0]);
1752
1753 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1754 setverdict(fail);
1755 }
1756 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1757 setverdict(fail);
1758 }
Philipp Maier36291392018-07-25 09:40:44 +02001759
1760 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001761
1762 setverdict(pass);
1763 }
1764
Philipp Maier1ac13982021-05-07 23:06:07 +02001765 /* Create one connection in loopback mode, test if the RTP packets are
1766 * actually reflected */
1767 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001768 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001769 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001770 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1771 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1772 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001773
1774 /* Same as above, but we will intenionally not tell the MGW where to
1775 * send the outgoing traffic. The connection is still created in
1776 * loopback mode, so the MGW should take the originating address from
1777 * the incoming RTP packet and send it back to the source */
1778 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001779 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001780 }
1781
1782
Philipp Maier7df85f62018-07-25 10:26:09 +02001783 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1784 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001785 var RtpFlowData flow[2];
1786 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001787 var MgcpResponse resp;
1788 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1789 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001790 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001791
1792 f_init(ep);
1793
1794 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001795 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001796 /* bind local RTP emulation sockets */
1797 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001798 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001799
1800 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001801 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001802 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001803 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1804
1805 if (bidir) {
1806 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1807 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1808
1809 /* Note: When we test bidirectional we may
1810 * loose packets during switch off because
1811 * both ends are transmitting and we only
1812 * can switch them off one by one. */
1813 tolerance := 3;
1814 } else {
1815 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1816 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1817 }
1818
1819 f_sleep(1.0);
1820
1821 f_flow_delete(RTPEM[1]);
1822 f_flow_delete(RTPEM[0], ep, call_id);
1823
1824 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1825 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1826 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1827 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001828 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001829 }
1830
Philipp Maier36291392018-07-25 09:40:44 +02001831 f_rtpem_stats_err_check(stats[0]);
1832 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001833
Philipp Maier2321ef92018-06-27 17:52:04 +02001834 setverdict(pass);
1835 }
1836
1837 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1838 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001839 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001840 }
1841
1842 /* create two local RTP emulations; create two connections on MGW EP,
1843 * exchange some data in both directions */
1844 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001845 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1846 }
1847
1848 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1849 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1850 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1851 }
1852
1853 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1854 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1855 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001856 }
1857
1858 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001859 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1860 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001861 var RtpFlowData flow[2];
1862 var RtpemStats stats[2];
1863 var MgcpResponse resp;
1864 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1865 var MgcpCallId call_id := '1227'H;
1866 var integer num_pkts_tx[2];
1867 var integer temp;
1868
1869 f_init(ep);
1870
1871 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001872 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001873 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001874 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001875
1876 /* Create the second connection. This connection will be also
1877 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001878 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001879 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001880 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001881
1882 /* The first leg starts transmitting */
1883 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1884 f_sleep(0.5);
1885 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1886 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001887 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001888 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001889 }
1890 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1891 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001892 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001893 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001894 }
1895
1896 /* The second leg starts transmitting a little later */
1897 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1898 f_sleep(1.0);
1899 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1900 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001901 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001902 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001903 }
1904 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1905 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001906 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001907 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001908 }
1909
1910 /* The first leg will now be switched into bidirectional
1911 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001912 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1913 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1914 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001915 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1916 f_sleep(0.5);
1917 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001918 if (stats[0].num_pkts_rx_err_disabled != 0) {
1919 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001920 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001921 }
1922 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1923 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001924 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001925 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001926 }
1927
1928 /* When the second leg is switched into bidirectional mode
1929 * as well, then the MGW will connect the two together and
1930 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02001931 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1932 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001933 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001934 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1935 f_sleep(2.0);
1936
1937 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1938 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1939
1940 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1941 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001942 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001943 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001944 }
1945
1946 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1947 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001948 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001949 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001950 }
Philipp Maier36291392018-07-25 09:40:44 +02001951
1952 f_rtpem_stats_err_check(stats[0]);
1953 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001954
1955 /* Tear down */
1956 f_flow_delete(RTPEM[0]);
1957 f_flow_delete(RTPEM[1], ep, call_id);
1958 setverdict(pass);
1959 }
1960
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001961 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1962 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1963 mp_local_ipv4, mp_remote_ipv4);
1964 }
1965
1966 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
1967 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
1968 mp_local_ipv6, mp_remote_ipv6);
1969 }
1970
1971 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
1972 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1973 mp_local_ipv6, mp_remote_ipv6);
1974 }
1975
Philipp Maier2321ef92018-06-27 17:52:04 +02001976 /* Test what happens when two RTP streams from different sources target
1977 * a single connection. Is the unsolicited stream properly ignored? */
1978 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1979 var RtpFlowData flow[2];
1980 var RtpemStats stats[2];
1981 var MgcpResponse resp;
1982 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1983 var MgcpCallId call_id := '1234321326'H;
1984 var integer unsolicited_port := 10002;
1985
1986 f_init(ep);
1987
1988 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001989 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001990 /* bind local RTP emulation sockets */
1991 flow[0].em.portnr := 10000;
1992 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1993
1994 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001995 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001996 flow[1].em.portnr := 20000;
1997 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001998
1999 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2000 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2001
Philipp Maier2321ef92018-06-27 17:52:04 +02002002 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002003
Philipp Maier2321ef92018-06-27 17:52:04 +02002004 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002005 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2006 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002007 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2008
2009 f_sleep(0.5);
2010
Daniel Willmanna069d382018-12-13 13:53:33 +01002011 /* Stop transmitting packets and tear down the flows */
2012 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002013 f_flow_delete(RTPEM[0]);
2014 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002015
2016 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2017 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2018 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2019 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002020 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002021 }
2022
Philipp Maier36291392018-07-25 09:40:44 +02002023 f_rtpem_stats_err_check(stats[0]);
2024 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002025
Harald Weltebb7523b2018-03-29 08:52:01 +02002026 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002027 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002028
Philipp Maier2321ef92018-06-27 17:52:04 +02002029 /* Test a handover situation. We first create two connections transmit
2030 * some data bidirectionally. Then we will simulate a handover situation. */
2031 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2032 var RtpFlowData flow[2];
2033 var RtpemStats stats[3];
2034 var MgcpResponse resp;
2035 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2036 var MgcpCallId call_id := '76338'H;
2037 var integer port_old;
2038
2039 f_init(ep);
2040
2041 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002042 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002043 /* bind local RTP emulation sockets */
2044 flow[0].em.portnr := 10000;
2045 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2046
2047 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002048 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002049 flow[1].em.portnr := 20000;
2050 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2051
2052 /* Normal rtp flow for one second */
2053 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2054 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2055 f_sleep(1.0);
2056
2057 /* Now switch the flow over to a new port (BTS) */
2058 port_old := flow[0].em.portnr;
2059 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002060 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002061 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002062 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002063
2064 /* When handing over a call, the old source may still keep
2065 * transmitting for a while. We simulate this by injecting
2066 * some unsolicited packets on the behalf of the old source,
2067 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002068 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2069 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002070 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2071 f_sleep(1.0);
2072 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2073 f_sleep(1.0);
2074
2075 /* Terminate call */
2076 f_flow_delete(RTPEM[0]);
2077 f_flow_delete(RTPEM[1], ep, call_id);
2078
2079 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2080 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2081 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2082 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002083 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002084 }
2085 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2086 if (stats[2].num_pkts_rx_err_disabled != 0) {
2087 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002088 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002089 }
2090
Philipp Maier36291392018-07-25 09:40:44 +02002091 f_rtpem_stats_err_check(stats[0]);
2092 f_rtpem_stats_err_check(stats[1]);
2093 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002094
Philipp Maier2321ef92018-06-27 17:52:04 +02002095 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002096 }
Harald Weltef53f1642017-11-18 19:57:11 +01002097
Philipp Maier6d4e0942019-02-21 17:35:01 +01002098
2099 /* create two local RTP emulations; create two connections on MGW EP, see if
2100 * exchanged data is converted bwtween ts101318 and rfc5993 */
2101 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2102 var RtpFlowData flow[2];
2103 var RtpemStats stats[2];
2104 var MgcpResponse resp;
2105 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2106 var MgcpCallId call_id := '1226'H;
2107
2108 f_init(ep);
2109
2110 /* Turn on conversion mode */
2111 f_vty_enter_config(MGWVTY);
2112 f_vty_transceive(MGWVTY, "mgcp");
2113 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2114
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002115 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002116 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002117 /* bind local RTP emulation sockets */
2118 flow[0].em.portnr := 10000;
2119 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2120 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2121 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2122 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2123 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2124
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002125 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002126 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002127 flow[1].em.portnr := 20000;
2128 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2129 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2130 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2131 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2132 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2133
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002134 /* Send RTP packets to connection #0, receive on connection #1 */
2135 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2136 f_sleep(0.5);
2137 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002138 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002139 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2140 f_sleep(0.5);
2141 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002142
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002143 /* Send RTP packets to connection #1, receive on connection #0 */
2144 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2145 f_sleep(0.5);
2146 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2147 f_sleep(1.0);
2148 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2149 f_sleep(0.5);
2150 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2151
2152 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002153 f_flow_delete(RTPEM[0]);
2154 f_flow_delete(RTPEM[1], ep, call_id);
2155
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002156 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002157 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2158 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002159 f_rtpem_stats_err_check(stats[0]);
2160 f_rtpem_stats_err_check(stats[1]);
2161
2162 /* Turn off conversion mode */
2163 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2164
2165 setverdict(pass);
2166 }
2167
Philipp Maier4f764ce2019-03-07 10:54:10 +01002168 /* create two local RTP emulations; create two connections on MGW EP, see if
2169 * exchanged data is converted between AMR octet-aligned and bandwith
2170 * efficient-mode */
2171 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2172 var RtpFlowData flow[2];
2173 var RtpemStats stats[2];
2174 var MgcpResponse resp;
2175 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2176 var MgcpCallId call_id := '1226'H;
2177
2178 f_init(ep);
2179
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002180 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002181 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002182 /* bind local RTP emulation sockets */
2183 flow[0].em.portnr := 10000;
2184 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2185 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2186 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2187 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2188 flow[0].fmtp := fmtp0;
2189 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2190
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002191 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002192 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002193 flow[1].em.portnr := 20000;
2194 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2195 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2196 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2197 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2198 flow[1].fmtp := fmtp1;
2199 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2200
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002201 /* Send RTP packets to connection #0, receive on connection #1 */
2202 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2203 f_sleep(0.5);
2204 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002205 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002206 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2207 f_sleep(0.5);
2208 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002209
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002210 /* Send RTP packets to connection #1, receive on connection #0 */
2211 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2212 f_sleep(0.5);
2213 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2214 f_sleep(1.0);
2215 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2216 f_sleep(0.5);
2217 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2218
2219 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002220 f_flow_delete(RTPEM[0]);
2221 f_flow_delete(RTPEM[1], ep, call_id);
2222
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002223 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002224 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2225 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002226 f_rtpem_stats_err_check(stats[0]);
2227 f_rtpem_stats_err_check(stats[1]);
2228
2229 setverdict(pass);
2230 }
2231
Philipp Maier882843d2020-05-25 15:33:13 +02002232 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2233 * functions are real world AMR RTP payloads including AMR header. The
2234 * payloads were extracted from a trace with known good payloads. */
2235
Philipp Maier4f764ce2019-03-07 10:54:10 +01002236 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002237 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002238 }
2239
2240 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2241 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2242 }
2243
2244 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2245 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2246 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002247
Harald Weltee636afd2017-09-17 16:24:09 +08002248 /* TODO: Double-DLCX (no retransmission) */
2249
2250
2251
2252 /* TODO: AUEP (various) */
2253 /* TODO: RSIP (various) */
2254 /* TODO: RQNT (various) */
2255 /* TODO: EPCF (various) */
2256 /* TODO: AUCX (various) */
2257 /* TODO: invalid verb (various) */
2258
Oliver Smith021141e2019-06-25 12:09:01 +02002259
2260 testcase TC_conn_timeout() runs on dummy_CT {
2261 var RtpFlowData flow;
2262 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2263 var MgcpCallId call_id := '1225'H;
2264 var MGCP_RecvFrom mrf;
2265
2266 f_init(ep);
2267 log("Setting conn-timeout to 1s");
2268 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2269
2270 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002271 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002272 flow.em.portnr := 10000;
2273 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2274 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2275 f_sleep(1.5);
2276
2277 log("Stopping for 0.5s and resuming");
2278 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2279 f_sleep(0.5);
2280 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2281 f_sleep(0.1);
2282
2283 log("Stopping for 1.5s, expecting to run into timeout");
2284 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2285 f_sleep(1.5);
2286
2287 log("Resuming should fail now");
2288 f_rtpem_conn_refuse_expect(RTPEM[0]);
2289 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2290 f_sleep(0.2);
2291 f_rtpem_conn_refuse_verify(RTPEM[0]);
2292
2293 setverdict(pass);
2294 }
2295
Philipp Maier2609c752020-07-08 12:38:09 +02002296 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2297 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2298 var template MgcpCommand cmd;
2299 var MgcpResponse resp;
2300 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2301 var MgcpCallId call_id := '8376F297'H;
2302
2303 f_init(ep);
2304
2305 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2306 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2307
2308 f_dlcx_ok(ep);
2309
2310 setverdict(pass);
2311 }
2312
2313 /* Test what happens when overlapping endpoints are selected (E1) */
2314 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2315 var template MgcpCommand cmd;
2316 var MgcpResponse resp;
2317 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2318 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2319 var MgcpCallId call_id_1 := '8376F297'H;
2320 var MgcpCallId call_id_2 := '837AF2A7'H;
2321
2322 f_init();
2323
2324 /* ep_1 and ep_2 are overlapping, selecting both one after
2325 * another should work fine: */
2326 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2327 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2328 f_dlcx_ok(ep_1);
2329 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2330 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2331 f_dlcx_ok(ep_2);
2332
2333 /* When ep_1 is serving a call we can not select ep_2 becaus
2334 * it is overlapping with ep_1 */
2335 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2336 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2337 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2338 resp := mgcp_transceive_mgw(cmd, ?);
2339 if (resp.line.code != "501") {
2340 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2341 }
2342 f_dlcx_ok(ep_1);
2343
2344 setverdict(pass);
2345 }
2346
2347 /* Create one connection in loopback mode, test if the RTP packets are
2348 * actually reflected */
2349 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2350 var RtpFlowData flow;
2351 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2352 var MgcpCallId call_id := '12250989'H;
2353 var RtpemStats stats;
2354
2355 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002356 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002357 flow.em.portnr := 10000;
2358 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2359
2360 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2361 f_sleep(1.0);
2362 f_flow_delete(RTPEM[0], ep, call_id);
2363
2364 stats := f_rtpem_stats_get(RTPEM[0]);
2365
2366 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2367 setverdict(fail);
2368 }
2369 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2370 setverdict(fail);
2371 }
2372
2373 f_rtpem_stats_err_check(stats);
2374
2375 setverdict(pass);
2376 }
2377
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002378 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2379 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2380 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2381 var template MgcpCommand cmd;
2382 var MgcpResponse resp;
2383 var MgcpCallId call_id := '1234'H;
2384 var MgcpConnectionId conn_id;
2385
2386 f_init(ep);
2387
2388 /* create the connection on the MGW */
2389 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2390 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2391 conn_id := extract_conn_id(resp);
2392
2393 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2394 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2395 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2396 valueof(ts_SDP_ptime(20)) });
2397 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2398
2399 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2400 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2401 }
2402 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2403 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2404 }
2405
2406 /* clean-up */
2407 f_dlcx_ok(ep, call_id);
2408 setverdict(pass);
2409 }
2410
2411 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2412 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2413 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2414 var template MgcpCommand cmd;
2415 var MgcpResponse resp;
2416 var MgcpCallId call_id := '1234'H;
2417 var MgcpConnectionId conn_id;
2418
2419 f_init(ep);
2420
2421 /* create the connection on the MGW */
2422 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2423 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2424 conn_id := extract_conn_id(resp);
2425
2426 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2427 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2428 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2429 valueof(ts_SDP_ptime(20)) });
2430 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2431
2432 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2433 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2434 }
2435 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2436 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2437 }
2438
2439 /* clean-up */
2440 f_dlcx_ok(ep, call_id);
2441 setverdict(pass);
2442 }
2443
Harald Welte00a067f2017-09-13 23:27:17 +02002444 control {
2445 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002446 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002447 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002448 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002449 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002450 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002451 execute(TC_crcx_early_bidir_mode());
2452 execute(TC_crcx_unsupp_param());
2453 execute(TC_crcx_missing_callid());
2454 execute(TC_crcx_missing_mode());
2455 execute(TC_crcx_unsupp_packet_intv());
2456 execute(TC_crcx_illegal_double_lco());
2457 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002458 execute(TC_crcx_wildcarded());
2459 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002460 execute(TC_mdcx_without_crcx());
2461 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002462 execute(TC_mdcx_wildcarded());
2463 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002464 execute(TC_crcx_and_dlcx_ep_callid_connid());
2465 execute(TC_crcx_and_dlcx_ep_callid());
2466 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002467 execute(TC_crcx_and_dlcx_ep_callid_inval());
2468 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002469 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002470
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002471 execute(TC_crcx_osmux_wildcard());
2472 execute(TC_crcx_osmux_fixed());
2473 execute(TC_crcx_osmux_fixed_twice());
2474 execute(TC_one_crcx_receive_only_osmux());
2475 execute(TC_one_crcx_loopback_osmux());
2476 execute(TC_two_crcx_and_rtp_osmux());
2477 execute(TC_two_crcx_and_rtp_osmux_bidir());
2478 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2479 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2480
Harald Welte33d82162017-12-28 03:21:57 +01002481 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002482
2483 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002484
2485 execute(TC_one_crcx_receive_only_rtp());
2486 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002487 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02002488 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002489 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002490 execute(TC_two_crcx_diff_pt_and_rtp());
2491 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002492 execute(TC_two_crcx_mdcx_and_rtp());
2493 execute(TC_two_crcx_and_unsolicited_rtp());
2494 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002495 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002496 execute(TC_amr_oa_bwe_rtp_conversion());
2497 execute(TC_amr_oa_oa_rtp_conversion());
2498 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002499
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002500 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002501
2502 execute(TC_e1_crcx_and_dlcx_ep());
2503 execute(TC_e1_crcx_with_overlap());
2504 execute(TC_e1_crcx_loopback());
2505
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002506 execute(TC_crcx_mdcx_ip4());
2507 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002508 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2509 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2510 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2511 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2512 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2513 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2514 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2515 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02002516
2517 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
2518 * older versions of osmo-mgw. This eventually leads into
2519 * a failure of all subsequent testcases, so it is important
2520 * not to add new testcaes after this one. */
2521 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02002522 }
2523}