blob: cbc22a627c962fbdd4b4292ec87d9cdc3154d165 [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 Pedrold7963bb2022-05-25 18:19:52 +020022 import from IuUP_Emulation all;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020023 import from OSMUX_Types all;
24 import from OSMUX_CodecPort all;
25 import from OSMUX_CodecPort_CtrlFunct all;
26 import from OSMUX_Emulation all;
Pau Espin Pedrol71ed4632022-09-02 18:51:19 +020027 import from AMR_Types all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080028 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010029 import from General_Types all;
30 import from Native_Functions all;
31 import from IPCP_Types all;
32 import from IP_Types all;
33 import from Osmocom_VTY_Functions all;
34 import from TELNETasp_PortType all;
Philipp Maier55b90542021-07-02 12:33:19 +020035 import from StatsD_Types all;
36 import from StatsD_CodecPort all;
37 import from StatsD_CodecPort_CtrlFunct all;
38 import from StatsD_Checker all;
Philipp Maier3560bd62021-08-19 11:52:23 +020039 import from Osmocom_CTRL_Functions all;
40 import from Osmocom_CTRL_Types all;
41 import from Osmocom_CTRL_Adapter all;
Harald Welte00a067f2017-09-13 23:27:17 +020042
Philipp Maierbb7a01c2018-02-01 12:32:57 +010043 const charstring c_mgw_domain := "mgw";
44 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
45
Harald Welte21ba5572017-09-19 17:55:05 +080046 /* any variables declared in the component will be available to
47 * all functions that 'run on' the named component, similar to
48 * class members in C++ */
Philipp Maier3560bd62021-08-19 11:52:23 +020049 type component dummy_CT extends StatsD_ConnHdlr, CTRL_Adapter_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080050 port MGCP_CODEC_PT MGCP;
51 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010052 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080053 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010054
Philipp Maier2321ef92018-06-27 17:52:04 +020055 var RTP_Emulation_CT vc_RTPEM[3];
56 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010057
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020058 var OSMUX_Emulation_CT vc_OsmuxEM;
59 port OsmuxEM_CTRL_PT OsmuxEM;
60
Philipp Maier6137c322019-02-20 16:13:41 +010061 port TELNETasp_PT MGWVTY;
Philipp Maier55b90542021-07-02 12:33:19 +020062
63 var StatsD_Checker_CT vc_STATSD;
Harald Welte00a067f2017-09-13 23:27:17 +020064 };
65
Harald Weltee1e18c52017-09-17 16:23:07 +080066 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
67 var MgcpTransId tid := int2str(g_trans_id);
68 g_trans_id := g_trans_id + 1;
69 return tid;
70 }
71
Harald Welte21ba5572017-09-19 17:55:05 +080072 /* all parameters declared here can be modified / overridden by
73 * the config file in the [MODULE_PARAMETERS] section. If no
74 * config file is used or the file doesn't specify them, the
75 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080076 modulepar {
77 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020078 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020079 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080080 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020081 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020082 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010083 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020084 PortNumber mp_local_osmux_port := 1985;
Philipp Maier55b90542021-07-02 12:33:19 +020085 PortNumber mp_mgw_statsd_port := 8125;
Philipp Maier3560bd62021-08-19 11:52:23 +020086 PortNumber mp_mgw_ctrl_port := 4267;
Harald Welte3c6ebb92017-09-16 00:56:57 +080087 }
88
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020089 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
90 /* Turn on conversion mode */
91 f_vty_enter_config(MGWVTY);
92 f_vty_transceive(MGWVTY, "mgcp");
93 if (osmux_on) {
94 f_vty_transceive(MGWVTY, "osmux on");
95 } else {
96 f_vty_transceive(MGWVTY, "osmux off");
97 }
98 f_vty_transceive(MGWVTY, "exit");
99 f_vty_transceive(MGWVTY, "exit");
100
101 }
102
103 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +0100104 map(self:MGWVTY, system:MGWVTY);
105 f_vty_set_prompts(MGWVTY);
106 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200107
108 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +0100109 }
110
Harald Weltebb7523b2018-03-29 08:52:01 +0200111 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
112 runs on dummy_CT {
113 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
114 map(comp_ref:RTP, system:RTP);
115 map(comp_ref:RTCP, system:RTCP);
116 comp_ref.start(RTP_Emulation.f_main());
117 }
118
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200119 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
120 runs on dummy_CT {
121 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
122 map(comp_ref:OSMUX, system:OSMUX);
123 comp_ref.start(OSMUX_Emulation.f_main());
124 }
125
Harald Welte21ba5572017-09-19 17:55:05 +0800126 /* initialization function, called by each test case at the
127 * beginning, but 'initialized' variable ensures its body is
128 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200129 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800130 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100131 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200132
Harald Welteedc45c12017-11-18 19:15:05 +0100133 if (initialized == false) {
134 initialized := true;
135
136 /* some random number for the initial transaction id */
137 g_trans_id := float2int(rnd()*65535.0);
138 map(self:MGCP, system:MGCP_CODEC_PT);
139 /* connect the MGCP test port using the given
140 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
141 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200142 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 +0200143 if (not ispresent(res.connId)) {
144 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200145 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200146 }
Harald Welteedc45c12017-11-18 19:15:05 +0100147 g_mgcp_conn_id := res.connId;
148
Harald Weltebb7523b2018-03-29 08:52:01 +0200149 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
150 f_rtpem_init(vc_RTPEM[i], i);
151 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
152 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200153 if (osmux_on) {
154 f_osmuxem_init(vc_OsmuxEM);
155 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
156 }
Philipp Maier55b90542021-07-02 12:33:19 +0200157
Philipp Maier2ff3e662021-08-19 10:52:33 +0200158 f_init_statsd("VirtCallAgent", vc_STATSD, mp_local_ipv4, mp_mgw_statsd_port);
Philipp Maier55b90542021-07-02 12:33:19 +0200159 connect(self:STATSD_PROC, vc_STATSD:STATSD_PROC);
160
161 f_statsd_reset();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800162 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800163
Philipp Maier3560bd62021-08-19 11:52:23 +0200164 f_ipa_ctrl_start_client(mp_remote_ipv4, mp_mgw_ctrl_port);
165
Harald Welteedc45c12017-11-18 19:15:05 +0100166 if (isvalue(ep)) {
167 /* do a DLCX on all connections of the EP */
168 f_dlcx_ignore(valueof(ep));
169 }
Philipp Maier6137c322019-02-20 16:13:41 +0100170
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200171 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800172 }
173
Harald Welte00a067f2017-09-13 23:27:17 +0200174 testcase TC_selftest() runs on dummy_CT {
175 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 +0100176 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 +0200177 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
178 "I: 1\n" &
179 "\n" &
180 "v=0\r\n" &
181 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
182 "s=-\r\n" &
183 "c=IN IP4 0.0.0.0\r\n" &
184 "t=0 0\r\n" &
185 "m=audio 0 RTP/AVP 126\r\n" &
186 "a=rtpmap:126 AMR/8000\r\n" &
187 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100188 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 +0200189 "M: sendrecv\r" &
190 "C: 2\r\n" &
191 "I: 1\r\n" &
192 "L: p:20, a:AMR, nt:IN\r\n" &
193 "\n" &
194 "v=0\r\n" &
195 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800196 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200197 "c=IN IP4 0.0.0.0\r\n" &
198 "t=0 0\r\n" &
199 "m=audio 4441 RTP/AVP 99\r\n" &
200 "a=rtpmap:99 AMR/8000\r\n" &
201 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800202 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200203
204 log(c_auep);
205 log(dec_MgcpCommand(c_auep));
206
207 log(c_mdcx3);
208 log(dec_MgcpCommand(c_mdcx3));
209
210 log(c_mdcx3_ret);
211 log(dec_MgcpResponse(c_mdcx3_ret));
212
213 log(c_mdcx4);
214 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800215
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100216 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
217 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 +0800218
219 log(c_crcx510_ret);
220 log(dec_MgcpResponse(c_crcx510_ret));
221 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100222
223 /* We didn't encounter any DTE, so pass the test */
224 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800225 }
226
Harald Weltee636afd2017-09-17 16:24:09 +0800227 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100228 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800229 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
230 * - CRCX with remote session description and without
231 *
232 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100233 * x packetization != 20ms
234 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800235 * x unsupported mode (517)
236 * x bidirectional mode before RemoteConnDesc: 527
237 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100238 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800239 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
240 */
241
Harald Welte21ba5572017-09-19 17:55:05 +0800242 /* build a receive template for receiving a MGCP message. You
243 * pass the MGCP response template in, and it will generate an
244 * MGCP_RecvFrom template that can match the primitives arriving on the
245 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800246 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
247 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100248 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200249 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800250 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200251 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800252 locPort := mp_local_udp_port,
253 msg := { response := resp }
254 }
255 return mrf;
256 }
257
258 /* Send a MGCP request + receive a (matching!) response */
259 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
260 var MgcpMessage msg := { command := valueof(cmd) };
261 resp.line.trans_id := cmd.line.trans_id;
262 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800263 var MGCP_RecvFrom mrf;
264 timer T := 5.0;
265
Harald Welte55015362017-11-18 16:02:42 +0100266 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800267 T.start;
268 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800269 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200270 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
271 setverdict(fail, "Response didn't match template");
272 mtc.stop;
273 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800274 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200275 [] T.timeout {
276 setverdict(fail, "Timeout waiting for response to ", cmd);
277 mtc.stop;
278 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800279 }
280 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800281
282 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
283 return mrf.msg.response;
284 } else {
285 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
286 return r;
287 }
Harald Welte00a067f2017-09-13 23:27:17 +0200288 }
289
Harald Welteba62c8c2017-11-18 18:26:49 +0100290 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
291 var integer i;
292 for (i := 0; i < lengthof(resp.params); i := i + 1) {
293 var MgcpParameter par := resp.params[i];
294 if (par.code == "I") {
295 return str2hex(par.val);
296 }
297 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200298 setverdict(fail, "Could not find conn id for MgcpReponse");
299 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100300 return '00000000'H;
301 }
302
Harald Welte10889c12017-11-18 19:40:31 +0100303 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
304 template MgcpCallId call_id := omit,
305 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100306 var template MgcpCommand cmd;
307 var MgcpResponse resp;
308 var template MgcpResponse rtmpl := {
309 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100310 code := ret_code,
311 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100312 },
313 params := *,
314 sdp := *
315 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100316 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100317 resp := mgcp_transceive_mgw(cmd, rtmpl);
318 }
319
Harald Welte10889c12017-11-18 19:40:31 +0100320 /* Send DLCX and expect OK response */
321 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
322 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100323 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100324 }
325
Harald Welteba62c8c2017-11-18 18:26:49 +0100326 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100327 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100328 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100329 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100330 }
331
Harald Weltebb7523b2018-03-29 08:52:01 +0200332 type record HostPort {
333 charstring hostname,
334 integer portnr optional
335 }
336 type record RtpFlowData {
337 HostPort em, /* emulation side */
338 HostPort mgw, /* mgw side */
339 uint7_t pt,
340 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200341 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100342 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200343 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
344 MgcpOsmuxCID osmux_cid optional,
345 MgcpOsmuxCID osmux_cid_response optional,
346 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100347 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200348 }
349
Philipp Maier2321ef92018-06-27 17:52:04 +0200350 /* Create an RTP flow (bidirectional, or receive-only) */
351 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 +0200352 boolean one_phase := true)
353 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200354 var template MgcpCommand cmd;
355 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100356 var SDP_attribute_list attributes;
357
358 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
359 if (isvalue(flow.fmtp)) {
360 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
361 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200362
363 /* bind local RTP emulation socket */
364 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
365
Philipp Maier28bb8292018-07-20 17:09:17 +0200366 /* configure rtp-emulation */
367 if (ispresent(flow.rtp_cfg)) {
368 f_rtpem_configure(pt, flow.rtp_cfg);
369 } else {
370 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
371 rtp_cfg.tx_payload_type := flow.pt
372 f_rtpem_configure(pt, rtp_cfg);
373 }
374
Harald Weltebb7523b2018-03-29 08:52:01 +0200375 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200376 /* Connect flow to MGW using a CRCX that also contains an SDP
377 * part that tells the MGW where we are listening for RTP streams
378 * that come from the MGW. We get a fully working connection in
379 * one go. */
380
381 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200382 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100383 flow.em.portnr, { int2str(flow.pt) }, attributes);
384
Harald Weltebb7523b2018-03-29 08:52:01 +0200385 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
386 flow.mgcp_conn_id := extract_conn_id(resp);
387 /* extract port number from response */
388 flow.mgw.portnr :=
389 resp.sdp.media_list[0].media_field.ports.port_number;
390 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200391 /* Create a half-open connection only. We do not tell the MGW
392 * where it can send RTP streams to us. This means this
393 * connection will only be able to receive but can not send
394 * data back to us. In order to turn the connection in a fully
395 * bi-directional one, a separate MDCX is needed. */
396
397 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200398 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
399 flow.mgcp_conn_id := extract_conn_id(resp);
400 /* extract MGW-side port number from response */
401 flow.mgw.portnr :=
402 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200403 }
404 /* finally, connect the emulation-side RTP socket to the MGW */
405 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
406 }
407
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200408 /* Create an Osmux flow (bidirectional, or receive-only) */
409 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
410 boolean one_phase := true)
411 runs on dummy_CT {
412 var template MgcpCommand cmd;
413 var MgcpResponse resp;
414 var SDP_attribute_list attributes;
415 var OsmuxTxHandle tx_hdl;
416 var OsmuxRxHandle rx_hdl;
417 var charstring cid_response;
418 var OsmuxCID cid_resp_parsed
419
420 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
421 if (isvalue(flow.fmtp)) {
422 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
423 }
424
425 /* bind local Osmux emulation socket */
426 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
427
428 /* configure osmux-emulation */
429 if (ispresent(flow.osmux_cfg)) {
430 f_osmuxem_configure(pt, flow.osmux_cfg);
431 } else {
432 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
433 f_osmuxem_configure(pt, osmux_cfg);
434 flow.osmux_cfg := osmux_cfg
435 }
436
437 if (one_phase) {
438 /* Connect flow to MGW using a CRCX that also contains an SDP
439 * part that tells the MGW where we are listening for Osmux streams
440 * that come from the MGW. We get a fully working connection in
441 * one go. */
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200442 if (flow.osmux_cid != -1) {
443 /* We may still want to negotiate osmux CID later at MDCX */
444 rx_hdl := c_OsmuxemDefaultRxHandle;
445 rx_hdl.cid := flow.osmux_cid;
446 f_osmuxem_register_rxhandle(pt, rx_hdl);
447 flow.osmux_cid_sent := true;
448 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200449 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
450 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
451 flow.em.portnr, { int2str(flow.pt) }, attributes);
452 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
453 flow.mgcp_conn_id := extract_conn_id(resp);
454 /* extract port number from response */
455 flow.mgw.portnr :=
456 resp.sdp.media_list[0].media_field.ports.port_number;
457 } else {
458 /* Create a half-open connection only. We do not tell the MGW
459 * where it can send Osmux streams to us. This means this
460 * connection will only be able to receive but can not send
461 * data back to us. In order to turn the connection in a fully
462 * bi-directional one, a separate MDCX is needed. */
463
464 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
465 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
466
467 flow.mgcp_conn_id := extract_conn_id(resp);
468 /* extract MGW-side port number from response */
469 flow.mgw.portnr :=
470 resp.sdp.media_list[0].media_field.ports.port_number;
471 }
472
473 /* extract Osmux CID we got assigned by the MGW */
474 var MgcpMessage resp_msg := {
475 response := resp
476 }
477
478 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
479 setverdict(fail, "No Osmux CID in MGCP response", resp);
480 mtc.stop;
481 }
482
483 /* Make sure response is no wildcard */
484 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
485 if (flow.osmux_cid_response == -1) {
486 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
487 mtc.stop;
488 }
489 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
490 f_osmuxem_register_txhandle(pt, tx_hdl);
491
492 /* finally, connect the emulation-side RTP socket to the MGW */
493 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
494 }
495
Philipp Maier2321ef92018-06-27 17:52:04 +0200496 /* Modify an existing RTP flow */
497 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
498 runs on dummy_CT {
499 var template MgcpCommand cmd;
500 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100501 var SDP_attribute_list attributes;
502
503 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
504 if (isvalue(flow.fmtp)) {
505 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
506 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200507
508 /* rebind local RTP emulation socket to the new address */
509 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
510
Philipp Maier28bb8292018-07-20 17:09:17 +0200511 /* reconfigure rtp-emulation */
512 if (ispresent(flow.rtp_cfg)) {
513 f_rtpem_configure(pt, flow.rtp_cfg);
514 } else {
515 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
516 rtp_cfg.tx_payload_type := flow.pt
517 f_rtpem_configure(pt, rtp_cfg);
518 }
519
Philipp Maier2321ef92018-06-27 17:52:04 +0200520 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
521 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
522 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100523 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200524 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
525
526 /* extract MGW-side port number from response. (usually this
527 * will not change, but thats is up to the MGW) */
528 flow.mgw.portnr :=
529 resp.sdp.media_list[0].media_field.ports.port_number;
530
531 /* reconnect the emulation-side RTP socket to the MGW */
532 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
533 }
534
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200535 /* Modify an existing Osmux flow */
536 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
537 runs on dummy_CT {
538 var template MgcpCommand cmd;
539 var MgcpResponse resp;
540 var SDP_attribute_list attributes;
541 var OsmuxRxHandle rx_hdl;
542 var charstring cid_response;
543 var OsmuxCID cid_resp_parsed
544
545 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
546 if (isvalue(flow.fmtp)) {
547 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
548 }
549
550 /* rebind local Osmux emulation socket to the new address */
551 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
552
553 /* configure osmux-emulation */
554 if (ispresent(flow.osmux_cfg)) {
555 f_osmuxem_configure(pt, flow.osmux_cfg);
556 } else {
557 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
558 f_osmuxem_configure(pt, osmux_cfg);
559 }
560
561 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
562 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
563 rx_hdl := c_OsmuxemDefaultRxHandle;
564 rx_hdl.cid := flow.osmux_cid;
565 f_osmuxem_register_rxhandle(pt, rx_hdl);
566 flow.osmux_cid_sent := true;
567 }
568
569 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
570 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
571 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
572 flow.em.portnr, { int2str(flow.pt) }, attributes);
573 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
574
575 /* extract MGW-side port number from response. (usually this
576 * will not change, but thats is up to the MGW) */
577 flow.mgw.portnr :=
578 resp.sdp.media_list[0].media_field.ports.port_number;
579
580 /* extract Osmux CID we got assigned by the MGW */
581 var MgcpMessage resp_msg := {
582 response := resp
583 }
584
585 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
586 setverdict(fail, "No Osmux CID in MGCP response", resp);
587 mtc.stop;
588 }
589
590 /* Make sure response is no wildcard */
591 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
592 if (cid_resp_parsed == -1) {
593 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
594 mtc.stop;
595 }
596 if (cid_resp_parsed != flow.osmux_cid_response) {
597 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
598 mtc.stop;
599 }
600
601 /* reconnect the emulation-side Osmux socket to the MGW */
602 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
603 }
604
Philipp Maier2321ef92018-06-27 17:52:04 +0200605 /* Delete an existing RTP flow */
606 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
607 runs on dummy_CT {
608 var template MgcpCommand cmd;
609 var MgcpResponse resp;
610
611 /* Switch off RTP flow */
612 f_rtpem_mode(pt, RTPEM_MODE_NONE);
613
614 /* Delete connection on MGW (if needed) */
615 if (isvalue(call_id) and isvalue(ep)) {
616 f_sleep(0.1);
617 f_dlcx_ok(valueof(ep), call_id);
618 }
619 }
620
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200621 /* Delete an existing Osmux flow */
622 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
623 runs on dummy_CT {
624 var template MgcpCommand cmd;
625 var MgcpResponse resp;
626
627 /* Switch off Osmux flow */
628 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
629
630 /* Delete connection on MGW (if needed) */
631 if (isvalue(call_id) and isvalue(ep)) {
632 f_sleep(0.1);
633 f_dlcx_ok(valueof(ep), call_id);
634 }
635 }
636
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100637 function f_crcx(charstring ep_prefix) runs on dummy_CT {
638 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800639 var template MgcpCommand cmd;
640 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100641 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800642
Harald Welteedc45c12017-11-18 19:15:05 +0100643 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800644
Harald Welteba62c8c2017-11-18 18:26:49 +0100645 /* create the connection on the MGW */
646 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100647 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100648 extract_conn_id(resp);
649
650 /* clean-up */
651 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100652 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100653
Philipp Maier45635f42018-06-05 17:28:02 +0200654 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
655 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
656 var template MgcpCommand cmd;
657 var MgcpResponse resp;
658 var MgcpCallId call_id := '1234'H;
659
660 f_init(ep);
661
662 /* create the connection on the MGW */
663 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
664 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
665 extract_conn_id(resp);
666
667 /* clean-up */
668 f_dlcx_ok(ep, call_id);
669
670 /* See also OS#2658: Even when we omit the LCO information, we
671 expect the MGW to pick a sane payload type for us. This
672 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200673 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200674 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200675 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200676 }
677
678 /* See also OS#2658: We also expect the MGW to assign a port
679 number to us. */
680 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
681 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200682 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200683 }
684 }
685
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200686 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
687 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
688 var template MgcpCommand cmd;
689 var MgcpResponse resp;
690 var MgcpCallId call_id := '1234'H;
691 var charstring cid_response;
692
693 if (run_init) {
694 f_init(ep, true);
695 }
696
697 /* create the connection on the MGW */
698 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
699 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
700 extract_conn_id(resp);
701
702 /* extract Osmux CID we got assigned by the MGW */
703 var MgcpMessage resp_msg := {
704 response := resp
705 }
706
707 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
708 setverdict(fail, "No Osmux CID in MGCP response", resp);
709 mtc.stop;
710 }
711
712 /* Make sure response is no wildcard */
713 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
714 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
715 mtc.stop;
716 }
717
718 /* clean-up */
719 f_dlcx_ok(ep, call_id);
720 }
721
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100722 /* test valid CRCX without SDP */
723 testcase TC_crcx() runs on dummy_CT {
724 f_crcx(c_mgw_ep_rtpbridge);
725 setverdict(pass);
726 }
727
Philipp Maier45635f42018-06-05 17:28:02 +0200728 /* test valid CRCX without SDP and LCO */
729 testcase TC_crcx_no_lco() runs on dummy_CT {
730 f_crcx_no_lco(c_mgw_ep_rtpbridge);
731 setverdict(pass);
732 }
733
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100734 /* test valid CRCX without SDP (older method without endpoint prefix) */
735 testcase TC_crcx_noprefix() runs on dummy_CT {
736 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800737 setverdict(pass);
738 }
739
740 /* test CRCX with unsupported mode, expect 517 */
741 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
742 var template MgcpCommand cmd;
743 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100744 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100745 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800746 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
747
Harald Welteedc45c12017-11-18 19:15:05 +0100748 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800749
Harald Welteba62c8c2017-11-18 18:26:49 +0100750 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800751 resp := mgcp_transceive_mgw(cmd, rtmpl);
752 setverdict(pass);
753 }
754
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200755 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
756 testcase TC_crcx_osmo_ign() runs on dummy_CT {
757 var template MgcpCommand cmd;
758 var MgcpResponse resp;
759 var MgcpEndpoint ep := "7@" & c_mgw_domain;
760 var MgcpCallId call_id := '3'H;
761
762 f_init(ep);
763
764 /* CRCX 1 7@mgw MGCP 1.0
765 C: 3
766 L: p:20, a:GSM-EFR, nt:IN
767 M: recvonly
768 X-Osmo-IGN: C
769 */
770
771 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
772 cmd.params := {ts_MgcpParCallId(call_id),
773 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
774 t_MgcpParConnMode("recvonly"),
775 t_MgcpParOsmoIGN("C")};
776 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
777 extract_conn_id(resp);
778
779 /* clean-up */
780 f_dlcx_ok(ep, call_id);
781 setverdict(pass);
782 }
783
Harald Welte21ba5572017-09-19 17:55:05 +0800784 /* test CRCX with early bi-directional mode, expect 527 as
785 * bi-diretional media can only be established once both local and
786 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800787 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
788 var template MgcpCommand cmd;
789 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100790 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100791 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800792 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
793
Harald Welteedc45c12017-11-18 19:15:05 +0100794 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800795
Harald Welteba62c8c2017-11-18 18:26:49 +0100796 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800797 resp := mgcp_transceive_mgw(cmd, rtmpl);
798 setverdict(pass);
799 }
800
801 /* test CRCX with unsupported Parameters */
802 testcase TC_crcx_unsupp_param() runs on dummy_CT {
803 var template MgcpCommand cmd;
804 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100805 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100806 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800807 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
808
Harald Welteedc45c12017-11-18 19:15:05 +0100809 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800810
Harald Welteba62c8c2017-11-18 18:26:49 +0100811 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100812 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
813 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
814
Harald Weltee636afd2017-09-17 16:24:09 +0800815 resp := mgcp_transceive_mgw(cmd, rtmpl);
816 setverdict(pass);
817 }
818
819 /* test CRCX with missing CallId */
820 testcase TC_crcx_missing_callid() runs on dummy_CT {
821 var template MgcpCommand cmd;
822 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100823 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100824 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800825
Harald Welteedc45c12017-11-18 19:15:05 +0100826 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800827
Harald Welteba62c8c2017-11-18 18:26:49 +0100828 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800829 cmd.params := {
830 t_MgcpParConnMode("recvonly"),
831 t_MgcpParLocConnOpt("p:20")
832 }
833 resp := mgcp_transceive_mgw(cmd, rtmpl);
834 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100835
Harald Weltee636afd2017-09-17 16:24:09 +0800836 }
837
838 /* test CRCX with missing Mode */
839 testcase TC_crcx_missing_mode() runs on dummy_CT {
840 var template MgcpCommand cmd;
841 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100842 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100843 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100844 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800845
Harald Welteedc45c12017-11-18 19:15:05 +0100846 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800847
Harald Welteba62c8c2017-11-18 18:26:49 +0100848 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800849 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100850 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800851 t_MgcpParLocConnOpt("p:20")
852 }
853 resp := mgcp_transceive_mgw(cmd, rtmpl);
854 setverdict(pass);
855 }
856
857 /* test CRCX with unsupported packetization interval */
858 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
859 var template MgcpCommand cmd;
860 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100861 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100862 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100863 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800864
Harald Welteedc45c12017-11-18 19:15:05 +0100865 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800866
Harald Welteba62c8c2017-11-18 18:26:49 +0100867 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100868 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800869 resp := mgcp_transceive_mgw(cmd, rtmpl);
870 setverdict(pass);
871 }
872
873 /* test CRCX with illegal double presence of local connection option */
874 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
875 var template MgcpCommand cmd;
876 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100877 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100878 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800879 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
880
Harald Welteedc45c12017-11-18 19:15:05 +0100881 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800882
Harald Welteba62c8c2017-11-18 18:26:49 +0100883 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100884 /* p:20 is permitted only once and not twice! */
885 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800886 resp := mgcp_transceive_mgw(cmd, rtmpl);
887 setverdict(pass);
888 }
889
890 /* test valid CRCX with valid SDP */
891 testcase TC_crcx_sdp() runs on dummy_CT {
892 var template MgcpCommand cmd;
893 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100894 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100895 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800896
Harald Welteedc45c12017-11-18 19:15:05 +0100897 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800898
Harald Welteba62c8c2017-11-18 18:26:49 +0100899 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800900 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
901 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
902 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100903 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100904
905 /* clean-up */
906 f_dlcx_ok(ep, call_id);
907
Harald Weltee636afd2017-09-17 16:24:09 +0800908 setverdict(pass);
909 }
910
Philipp Maier5e06cee2018-02-01 18:28:08 +0100911 /* test valid wildcarded CRCX */
912 testcase TC_crcx_wildcarded() runs on dummy_CT {
913 var template MgcpCommand cmd;
914 var MgcpResponse resp;
915 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
916 var MgcpCallId call_id := '1234'H;
917 var MgcpEndpoint ep_assigned;
918 f_init();
919
920 /* create the connection on the MGW */
921 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
922 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
923 extract_conn_id(resp);
924
925 /* extract endpoint name we got assigned by the MGW */
926 var MgcpMessage resp_msg := {
927 response := resp
928 }
929 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
930 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200931 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100932 }
933
934 /* clean-up */
935 f_dlcx_ok(ep_assigned, call_id);
936
937 setverdict(pass);
938 }
939
940 /* test valid wildcarded CRCX */
941 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maierc60e8472020-07-08 12:57:13 +0200942 const integer n_endpoints := 31;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100943 var integer i;
944 var template MgcpCommand cmd;
945 var MgcpResponse resp;
946 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
947 var MgcpCallId call_id := '1234'H;
948 var MgcpEndpoint ep_assigned[n_endpoints];
949 f_init();
950
951 /* Exhaust all endpoint resources on the virtual trunk */
952 for (i := 0; i < n_endpoints; i := i+1) {
953 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
954 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
955
956 /* Make sure we got a connection id */
957 extract_conn_id(resp);
958
959 var MgcpMessage resp_msg := {
960 response := resp
961 }
962 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
963 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200964 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100965 }
966 }
967
968 /* Try to allocate one more endpoint, which should fail */
969 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
970 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
971 resp := mgcp_transceive_mgw(cmd, rtmpl);
972 setverdict(pass);
973
974 /* clean-up */
975 for (i := 0; i < n_endpoints; i := i+1) {
976 f_dlcx_ok(ep_assigned[i], call_id);
977 }
978 setverdict(pass);
979 }
980
Harald Weltee636afd2017-09-17 16:24:09 +0800981 /* TODO: various SDP related bits */
982
983
984 /* TODO: CRCX with X-Osmux */
985 /* TODO: double CRCX without force_realloc */
986
987 /* TODO: MDCX (various) */
988
989 /* TODO: MDCX without CRCX first */
990 testcase TC_mdcx_without_crcx() runs on dummy_CT {
991 var template MgcpCommand cmd;
992 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100993 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100994 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800995 var template MgcpResponse rtmpl := {
996 line := {
997 /* TODO: accept/enforce better error? */
998 code := "400",
999 string := ?
1000 },
1001 params:= { },
1002 sdp := omit
1003 };
1004
Harald Welteedc45c12017-11-18 19:15:05 +01001005 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001006
Harald Welte2bcfd3a2017-11-18 22:14:35 +01001007 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +08001008 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1009 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1010 valueof(ts_SDP_ptime(20)) });
1011 resp := mgcp_transceive_mgw(cmd, rtmpl);
1012 setverdict(pass);
1013 }
1014
1015 /* DLCX without CRCX first */
1016 testcase TC_dlcx_without_crcx() runs on dummy_CT {
1017 var template MgcpCommand cmd;
1018 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001019 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001020 var template MgcpResponse rtmpl := {
1021 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001022 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001023 string := ?
1024 },
1025 params:= { },
1026 sdp := omit
1027 };
1028
Harald Welteedc45c12017-11-18 19:15:05 +01001029 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001030
Harald Welteedc45c12017-11-18 19:15:05 +01001031 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001032 resp := mgcp_transceive_mgw(cmd, rtmpl);
1033 setverdict(pass);
1034 }
1035
Philipp Maier21c1cff2021-07-20 14:22:53 +02001036 /* DLCX to non existing endpoint */
1037 testcase TC_dlcx_non_existant_ep() runs on dummy_CT {
1038 var template MgcpCommand cmd;
1039 var MgcpResponse resp;
1040 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "AA@" & c_mgw_domain;
1041 var template MgcpResponse rtmpl := {
1042 line := {
1043 code := ("500"),
1044 string := ?
1045 },
1046 params:= { },
1047 sdp := omit
1048 };
1049
1050 f_init(ep);
1051
1052 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1053 resp := mgcp_transceive_mgw(cmd, rtmpl);
1054 setverdict(pass);
1055 }
1056
Philipp Maier8a3dc922018-02-02 14:55:12 +01001057 /* test valid wildcarded MDCX */
1058 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1059 /* Note: A wildcarded MDCX is not allowed, so we expect the
1060 * MGW to reject this request */
1061 var template MgcpCommand cmd;
1062 var MgcpResponse resp;
1063 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1064 var MgcpCallId call_id := '1225'H;
1065 var template MgcpResponse rtmpl := {
1066 line := {
1067 /* TODO: accept/enforce better error? */
1068 code := "507",
1069 string := ?
1070 },
1071 params:= { },
1072 sdp := omit
1073 };
1074
1075 f_init(ep);
1076
1077 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1078 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1079 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1080 valueof(ts_SDP_ptime(20)) });
1081 resp := mgcp_transceive_mgw(cmd, rtmpl);
1082 setverdict(pass);
1083 }
1084
1085 /* test valid wildcarded DLCX */
1086 testcase TC_dlcx_wildcarded() runs on dummy_CT {
Philipp Maier8a3dc922018-02-02 14:55:12 +01001087 var template MgcpCommand cmd;
1088 var MgcpResponse resp;
1089 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
Philipp Maier55b90542021-07-02 12:33:19 +02001090 const integer n_endpoints := 31;
1091 var integer i;
1092 var MgcpCallId call_id := '1234'H;
1093 var StatsDExpects expect;
1094 f_init(ep);
1095
1096 /* Allocate a few endpoints */
1097 for (i := 0; i < n_endpoints; i := i+1) {
1098 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1099 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1100 }
1101
Philipp Maier1298b092021-11-18 18:33:39 +01001102 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
1103 * occupied endpoints */
1104 f_sleep(1.0)
Philipp Maier55b90542021-07-02 12:33:19 +02001105 expect := {
1106 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := n_endpoints, max := n_endpoints}
1107 };
1108 f_statsd_expect(expect);
1109
1110 /* Send wildcarded DLCX */
Philipp Maier8a3dc922018-02-02 14:55:12 +01001111 var template MgcpResponse rtmpl := {
1112 line := {
Philipp Maier55b90542021-07-02 12:33:19 +02001113 code := "200",
Philipp Maier8a3dc922018-02-02 14:55:12 +01001114 string := ?
1115 },
1116 params:= { },
1117 sdp := omit
1118 };
Philipp Maier55b90542021-07-02 12:33:19 +02001119 cmd := ts_DLCX(get_next_trans_id(), ep);
1120 mgcp_transceive_mgw(cmd, rtmpl);
Philipp Maier8a3dc922018-02-02 14:55:12 +01001121
Philipp Maier6c740e82022-06-30 12:04:34 +02001122 /* Query a the statsd once to ensure that intermediate results are pulled from the
1123 * pipeline. The second query (below) will return the actual result. */
1124 expect := {
1125 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := n_endpoints}
1126 };
1127 f_statsd_expect(expect);
1128
1129 /* The second query must resturn a result with 0 endpoints in use. */
Philipp Maier55b90542021-07-02 12:33:19 +02001130 expect := {
1131 { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0}
1132 };
1133 f_statsd_expect(expect);
1134
Philipp Maier8a3dc922018-02-02 14:55:12 +01001135 setverdict(pass);
1136 }
1137
Harald Welte79181ff2017-11-18 19:26:11 +01001138 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1139 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1140 var template MgcpCommand cmd;
1141 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001142 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001143 var MgcpCallId call_id := '51234'H;
1144
1145 f_init(ep);
1146
1147 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1148 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1149
1150 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1151
1152 setverdict(pass);
1153 }
1154
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001155 /* test valid CRCX without SDP */
1156 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1157 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1158 setverdict(pass);
1159 }
1160
1161 /* test valid CRCX without SDP */
1162 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1163 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1164 setverdict(pass);
1165 }
1166
1167 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1168 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1169 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1170 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1171 setverdict(pass);
1172 }
1173
1174 /* Create one half open connection in receive-only mode. The MGW must accept
1175 * the packets but must not send any. */
1176 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1177 var RtpFlowData flow;
1178 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1179 var MgcpCallId call_id := '1225'H;
1180 var OsmuxemStats stats;
1181 var OsmuxTxHandle tx_hdl;
1182
1183 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001184 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001185 flow.em.portnr := mp_local_osmux_port;
1186 flow.osmux_cid := -1;
1187 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1188
1189 /* create a transmitter not yet known by MGW */
1190 tx_hdl := valueof(t_TxHandleAMR590(2));
1191 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1192
1193 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1194 f_sleep(1.0);
1195 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1196
1197 stats := f_osmuxem_stats_get(OsmuxEM);
1198
1199 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1200 setverdict(fail);
1201 }
1202 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1203 setverdict(fail);
1204 }
1205
1206 f_osmuxem_stats_err_check(stats);
1207
1208 setverdict(pass);
1209 }
1210
1211 /* Create one connection in loopback mode, test if the Osmux packets are
1212 * actually reflected */
1213 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1214 var RtpFlowData flow;
1215 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1216 var MgcpCallId call_id := '1225'H;
1217 var OsmuxemStats stats;
1218 var OsmuxTxHandle tx_hdl;
1219
1220 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001221 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001222 flow.em.portnr := mp_local_osmux_port;
1223 flow.osmux_cid := 2;
1224 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1225
1226 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1227 f_sleep(1.0);
1228
1229 /* Switch off both Tx, wait to receive delayed frames from MGW */
1230 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1231 f_sleep(0.1);
1232 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1233
1234 stats := f_osmuxem_stats_get(OsmuxEM);
1235
1236 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1237 setverdict(fail);
1238 }
1239 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1240 setverdict(fail);
1241 }
1242
1243 f_osmuxem_stats_err_check(stats);
1244
1245 setverdict(pass);
1246 }
1247
1248 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1249 * must match the reception statistics on the other side and vice versa. The
1250 * user may also supply a tolerance value (number of packets) when deviations
1251 * are acceptable */
1252 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1253 var integer plen;
1254
1255 log("stats A: ", a);
1256 log("stats B: ", b);
1257 log("tolerance: ", tolerance, " packets");
1258 log("batch_size: ", batch_size, " packets");
1259
1260 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1261
1262 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1263 return false;
1264 }
1265
1266 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1267 return false;
1268 }
1269
1270 if(a.num_pkts_tx > 0) {
1271 plen := a.bytes_payload_tx / a.num_pkts_tx;
1272 } else {
1273 plen := 0;
1274 }
1275
1276 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1277 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1278 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1279 return false;
1280 }
1281
1282 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) {
1283 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1284 return false;
1285 }
1286
1287 return true;
1288 }
1289
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001290 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1291 charstring local_ip_rtp, charstring remote_ip_rtp,
1292 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001293 var RtpFlowData flow[2];
1294 var RtpemStats stats_rtp;
1295 var OsmuxemStats stats_osmux;
1296 var MgcpResponse resp;
1297 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1298 var MgcpCallId call_id := '1226'H;
1299 var integer tolerance := 0;
1300
1301 f_init(ep, true);
1302
1303 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001304 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001305 flow[0].rtp_cfg := c_RtpemDefaultCfg
1306 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1307 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1308 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);
1309 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1310 /* bind local RTP emulation sockets */
1311 flow[0].em.portnr := 10000;
1312 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1313
1314 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001315 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001316 flow[1].em.portnr := mp_local_osmux_port;
1317 flow[1].osmux_cid := 2;
1318 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1319 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1320
1321 if (bidir) {
1322 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1323 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1324
1325 /* Note: When we test bidirectional we may
1326 * loose packets during switch off because
1327 * both ends are transmitting and we only
1328 * can switch them off one by one. */
1329 tolerance := 3;
1330 } else {
1331 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1332 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1333 }
1334
1335 f_sleep(1.0);
1336
1337 /* Switch off both Tx, wait to receive delayed frames from MGW */
1338 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1339 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1340 f_sleep(0.1);
1341
1342 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1343 f_flow_delete(RTPEM[1]);
1344
1345 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1346 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1347 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1348 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1349 mtc.stop;
1350 }
1351
1352 f_rtpem_stats_err_check(stats_rtp);
1353 f_osmuxem_stats_err_check(stats_osmux);
1354
1355 setverdict(pass);
1356 }
1357
1358 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1359 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001360 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1361 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001362 }
1363
1364 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1365 * exchange some data in both directions */
1366 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001367 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1368 mp_local_ipv4, mp_remote_ipv4);
1369 }
1370
1371 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1372 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1373 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1374 mp_local_ipv6, mp_remote_ipv6);
1375 }
1376 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1377 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1378 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1379 mp_local_ipv6, mp_remote_ipv6);
1380 }
1381 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1382 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1383 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1384 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001385 }
1386
1387
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001388 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1389 charstring local_ip_rtp, charstring remote_ip_rtp,
1390 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001391 var RtpFlowData flow[2];
1392 var RtpemStats stats_rtp;
1393 var OsmuxemStats stats_osmux;
1394 var MgcpResponse resp;
1395 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1396 var MgcpCallId call_id := '1227'H;
1397 var integer num_pkts_tx[2];
1398 var integer temp;
1399
1400 f_init(ep, true);
1401
1402 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001403 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001404 flow[0].rtp_cfg := c_RtpemDefaultCfg
1405 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1406 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1407 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);
1408 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1409 /* bind local RTP emulation sockets */
1410 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001411 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001412
1413
1414 /* Create the second connection. This connection will be also
1415 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001416 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001417 flow[1].em.portnr := mp_local_osmux_port;
1418 if (crcx_osmux_wildcard) {
1419 flow[1].osmux_cid := -1;
1420 } else {
1421 flow[1].osmux_cid := 2;
1422 }
1423 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001424 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001425
1426
1427 /* The first leg starts transmitting */
1428 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1429 f_sleep(0.5);
1430 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1431 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1432 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1433 mtc.stop;
1434 }
1435 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1436 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1437 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1438 mtc.stop;
1439 }
1440
1441 /* The second leg starts transmitting a little later */
1442 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1443 f_sleep(1.0);
1444 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1445 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1446 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1447 mtc.stop;
1448 }
1449 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1450 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1451 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1452 mtc.stop;
1453 }
1454
1455 /* The first leg will now be switched into bidirectional
1456 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001457 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1458 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1459 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001460 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1461 f_sleep(0.5);
1462 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1463 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1464 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1465 mtc.stop;
1466 }
1467 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1468 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1469 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1470 mtc.stop;
1471 }
1472
1473 /* When the second leg is switched into bidirectional mode
1474 * as well, then the MGW will connect the two together and
1475 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001476 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1477 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001478 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001479
1480 if (crcx_osmux_wildcard) {
1481 /* For now we must set same CID as the MGW recvCID,
1482 * having sendCID!=recvCID is not yet supported. */
1483 flow[1].osmux_cid := flow[1].osmux_cid_response;
1484 }
1485 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1486 f_sleep(2.0);
1487
1488 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1489 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1490
1491 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1492 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1493 log("stats_rtp: ", stats_rtp);
1494 log("stats_osmux: ", stats_osmux);
1495 log("old_rtp_tx: ", num_pkts_tx[0]);
1496 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1497 mtc.stop;
1498 }
1499
1500 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1501 if (temp > 3 or temp < -3) {
1502 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1503 mtc.stop;
1504 }
1505
1506 f_rtpem_stats_err_check(stats_rtp);
1507 f_osmuxem_stats_err_check(stats_osmux);
1508
1509 /* Tear down */
1510 f_flow_delete(RTPEM[0]);
1511 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1512 setverdict(pass);
1513 }
1514
1515 /* create one RTP and one OSmux emulations and pass data in both
1516 directions. Create CRCX with wildcard Osmux CID and set it later
1517 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1518 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001519 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1520 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001521 }
1522
1523 /* create one RTP and one OSmux emulations and pass data in both
1524 directions. Create CRCX with fixed Osmux CID and keep it during
1525 MDCX. This is similar to how BSC sets up the call in AoIP. */
1526 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001527 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1528 mp_local_ipv4, mp_remote_ipv4);
1529 }
1530
1531 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1532 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1533 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1534 mp_local_ipv6, mp_remote_ipv6);
1535 }
1536 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1537 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1538 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1539 mp_local_ipv6, mp_remote_ipv6);
1540 }
1541 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1542 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1543 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1544 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001545 }
1546
Harald Welte646ecdb2017-12-28 03:21:57 +01001547 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1548 var template MgcpCommand cmd;
1549 var MgcpResponse resp;
1550
1551 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1552 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1553
1554 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1555
1556 setverdict(pass);
1557 }
1558
1559 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1560 var MgcpEndpoint ep;
1561 var MgcpCallId call_id;
1562 var integer ep_nr;
1563
1564 f_init();
1565
1566 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001567 if(ep_nr > 15) {
1568 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1569 } else {
1570 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1571 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001572 call_id := int2hex(ep_nr, 2) & '1234'H;
1573 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1574 }
1575 }
1576
Harald Welte79181ff2017-11-18 19:26:11 +01001577 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1578 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001579 var template MgcpCommand cmd;
1580 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001581 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001582 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001583
Harald Welteedc45c12017-11-18 19:15:05 +01001584 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001585
Harald Welteba62c8c2017-11-18 18:26:49 +01001586 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001587 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001588
Harald Welteba62c8c2017-11-18 18:26:49 +01001589 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001590
1591 setverdict(pass);
1592 }
1593
Harald Welte79181ff2017-11-18 19:26:11 +01001594 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1595 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1596 var template MgcpCommand cmd;
1597 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001598 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001599 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001600
1601 f_init(ep);
1602
1603 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1604 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1605
1606 f_dlcx_ok(ep);
1607
1608 setverdict(pass);
1609 }
1610
1611
Harald Welte6d167f82017-11-18 19:41:35 +01001612 /* CRCX + DLCX of valid endpoint but invalid call-id */
1613 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1614 var template MgcpCommand cmd;
1615 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001616 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001617 var MgcpCallId call_id := '51231'H;
1618
1619 f_init(ep);
1620
1621 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1622 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1623
1624 f_dlcx(ep, "516", *, 'ffff'H);
1625
1626 setverdict(pass);
1627 }
1628
1629
1630 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1631 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1632 var template MgcpCommand cmd;
1633 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001634 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001635 var MgcpCallId call_id := '51230'H;
1636
1637 f_init(ep);
1638
1639 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1640 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1641
1642 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1643
1644 setverdict(pass);
1645 }
1646
1647
Harald Weltee636afd2017-09-17 16:24:09 +08001648 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001649 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1650 var template MgcpCommand cmd;
1651 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001652 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001653 var MgcpCallId call_id := '51229'H;
1654 var template MgcpResponse rtmpl := {
1655 line := {
1656 code := "200",
1657 string := "OK"
1658 },
1659 params:= { },
1660 sdp := omit
1661 };
1662
1663 f_init(ep);
1664
1665 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1666 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1667
1668 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1669 resp := mgcp_transceive_mgw(cmd, rtmpl);
1670 resp := mgcp_transceive_mgw(cmd, rtmpl);
1671
1672 setverdict(pass);
1673 }
1674
Harald Weltebb7523b2018-03-29 08:52:01 +02001675 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1676 charstring codec) := {
1677 em := {
1678 hostname := host_a,
1679 portnr := omit
1680 },
1681 mgw := {
1682 hostname := host_b,
1683 portnr := omit
1684 },
1685 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001686 codec := codec,
1687 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001688 }
Harald Weltef53f1642017-11-18 19:57:11 +01001689
Harald Weltebb7523b2018-03-29 08:52:01 +02001690 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1691 testcase TC_rtpem_selftest() runs on dummy_CT {
1692 var RtpemStats stats[2];
1693 var integer local_port := 10000;
1694 var integer local_port2 := 20000;
1695
1696 f_init();
1697
1698 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1699 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1700
1701 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1702 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1703
1704 log("=== starting");
1705 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1706 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1707
1708 f_sleep(5.0);
1709
1710 log("=== stopping");
1711 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1712 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1713 f_sleep(0.5);
1714 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1715 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1716
1717 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1718 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1719 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1720 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001721 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001722 }
1723 setverdict(pass);
1724 }
1725
Philipp Maier2321ef92018-06-27 17:52:04 +02001726 /* Create one half open connection in receive-only mode. The MGW must accept
1727 * the packets but must not send any. */
1728 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1729 var RtpFlowData flow;
1730 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1731 var MgcpCallId call_id := '1225'H;
1732 var RtpemStats stats;
1733
1734 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001735 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001736 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001737 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001738
1739 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1740 f_sleep(1.0);
1741 f_flow_delete(RTPEM[0], ep, call_id);
1742
1743 stats := f_rtpem_stats_get(RTPEM[0]);
1744
Philipp Maier42b17cc2019-10-01 13:53:17 +02001745 /* Make sure that at least some amount of RTP packets/bytes
1746 * have has been transmitted. The compare values for
1747 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1748 * using a testrun and the results were devided by 2, so even
1749 * in load situations we should reach the minimum amount of
1750 * required packets/bytes */
1751
1752 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001753 setverdict(fail);
1754 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001755 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001756 setverdict(fail);
1757 }
Philipp Maier36291392018-07-25 09:40:44 +02001758
1759 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001760
1761 setverdict(pass);
1762 }
1763
1764 /* Create one connection in loopback mode, test if the RTP packets are
1765 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001766 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 +02001767 var RtpFlowData flow;
1768 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1769 var MgcpCallId call_id := '1225'H;
1770 var RtpemStats stats;
1771
1772 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001773 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001774 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001775 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001776
1777 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1778 f_sleep(1.0);
1779 f_flow_delete(RTPEM[0], ep, call_id);
1780
1781 stats := f_rtpem_stats_get(RTPEM[0]);
1782
1783 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1784 setverdict(fail);
1785 }
1786 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1787 setverdict(fail);
1788 }
Philipp Maier36291392018-07-25 09:40:44 +02001789
1790 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001791
1792 setverdict(pass);
1793 }
1794
Philipp Maier1ac13982021-05-07 23:06:07 +02001795 /* Create one connection in loopback mode, test if the RTP packets are
1796 * actually reflected */
1797 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001798 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001799 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001800 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1801 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1802 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001803
1804 /* Same as above, but we will intenionally not tell the MGW where to
1805 * send the outgoing traffic. The connection is still created in
1806 * loopback mode, so the MGW should take the originating address from
1807 * the incoming RTP packet and send it back to the source */
1808 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001809 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001810 }
1811
1812
Philipp Maier7df85f62018-07-25 10:26:09 +02001813 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1814 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001815 var RtpFlowData flow[2];
1816 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001817 var MgcpResponse resp;
1818 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1819 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001820 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001821
1822 f_init(ep);
1823
1824 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001825 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001826 /* bind local RTP emulation sockets */
1827 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001828 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001829
1830 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001831 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001832 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001833 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1834
1835 if (bidir) {
1836 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1837 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1838
1839 /* Note: When we test bidirectional we may
1840 * loose packets during switch off because
1841 * both ends are transmitting and we only
1842 * can switch them off one by one. */
1843 tolerance := 3;
1844 } else {
1845 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1846 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1847 }
1848
1849 f_sleep(1.0);
1850
1851 f_flow_delete(RTPEM[1]);
1852 f_flow_delete(RTPEM[0], ep, call_id);
1853
1854 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1855 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1856 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1857 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001858 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001859 }
1860
Philipp Maier36291392018-07-25 09:40:44 +02001861 f_rtpem_stats_err_check(stats[0]);
1862 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001863
Philipp Maier2321ef92018-06-27 17:52:04 +02001864 setverdict(pass);
1865 }
1866
1867 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1868 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001869 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001870 }
1871
1872 /* create two local RTP emulations; create two connections on MGW EP,
1873 * exchange some data in both directions */
1874 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001875 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1876 }
1877
1878 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1879 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1880 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1881 }
1882
1883 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1884 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1885 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001886 }
1887
1888 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001889 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1890 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001891 var RtpFlowData flow[2];
1892 var RtpemStats stats[2];
1893 var MgcpResponse resp;
1894 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1895 var MgcpCallId call_id := '1227'H;
1896 var integer num_pkts_tx[2];
1897 var integer temp;
1898
1899 f_init(ep);
1900
1901 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001902 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001903 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001904 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001905
1906 /* Create the second connection. This connection will be also
1907 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001908 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001909 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001910 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001911
1912 /* The first leg starts transmitting */
1913 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1914 f_sleep(0.5);
1915 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1916 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001917 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001918 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001919 }
1920 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1921 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001922 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001923 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001924 }
1925
1926 /* The second leg starts transmitting a little later */
1927 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1928 f_sleep(1.0);
1929 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1930 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001931 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001932 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001933 }
1934 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1935 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001936 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001937 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001938 }
1939
1940 /* The first leg will now be switched into bidirectional
1941 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001942 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1943 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1944 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001945 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1946 f_sleep(0.5);
1947 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001948 if (stats[0].num_pkts_rx_err_disabled != 0) {
1949 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001950 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001951 }
1952 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1953 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001954 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001955 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001956 }
1957
1958 /* When the second leg is switched into bidirectional mode
1959 * as well, then the MGW will connect the two together and
1960 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02001961 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1962 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001963 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001964 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1965 f_sleep(2.0);
1966
1967 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1968 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1969
1970 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1971 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001972 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001973 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001974 }
1975
1976 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1977 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001978 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001979 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001980 }
Philipp Maier36291392018-07-25 09:40:44 +02001981
1982 f_rtpem_stats_err_check(stats[0]);
1983 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001984
1985 /* Tear down */
1986 f_flow_delete(RTPEM[0]);
1987 f_flow_delete(RTPEM[1], ep, call_id);
1988 setverdict(pass);
1989 }
1990
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001991 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1992 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1993 mp_local_ipv4, mp_remote_ipv4);
1994 }
1995
1996 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
1997 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
1998 mp_local_ipv6, mp_remote_ipv6);
1999 }
2000
2001 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
2002 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
2003 mp_local_ipv6, mp_remote_ipv6);
2004 }
2005
Philipp Maier2321ef92018-06-27 17:52:04 +02002006 /* Test what happens when two RTP streams from different sources target
2007 * a single connection. Is the unsolicited stream properly ignored? */
2008 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
2009 var RtpFlowData flow[2];
2010 var RtpemStats stats[2];
2011 var MgcpResponse resp;
2012 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2013 var MgcpCallId call_id := '1234321326'H;
2014 var integer unsolicited_port := 10002;
2015
2016 f_init(ep);
2017
2018 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002019 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002020 /* bind local RTP emulation sockets */
2021 flow[0].em.portnr := 10000;
2022 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2023
2024 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002025 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002026 flow[1].em.portnr := 20000;
2027 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02002028
2029 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2030 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2031
Philipp Maier2321ef92018-06-27 17:52:04 +02002032 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02002033
Philipp Maier2321ef92018-06-27 17:52:04 +02002034 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002035 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
2036 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002037 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2038
2039 f_sleep(0.5);
2040
Daniel Willmanna069d382018-12-13 13:53:33 +01002041 /* Stop transmitting packets and tear down the flows */
2042 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02002043 f_flow_delete(RTPEM[0]);
2044 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02002045
2046 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2047 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2048 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
2049 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002050 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02002051 }
2052
Philipp Maier36291392018-07-25 09:40:44 +02002053 f_rtpem_stats_err_check(stats[0]);
2054 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02002055
Harald Weltebb7523b2018-03-29 08:52:01 +02002056 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02002057 }
Harald Weltebb7523b2018-03-29 08:52:01 +02002058
Philipp Maier2321ef92018-06-27 17:52:04 +02002059 /* Test a handover situation. We first create two connections transmit
2060 * some data bidirectionally. Then we will simulate a handover situation. */
2061 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
2062 var RtpFlowData flow[2];
2063 var RtpemStats stats[3];
2064 var MgcpResponse resp;
2065 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
2066 var MgcpCallId call_id := '76338'H;
2067 var integer port_old;
2068
2069 f_init(ep);
2070
2071 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002072 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002073 /* bind local RTP emulation sockets */
2074 flow[0].em.portnr := 10000;
2075 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2076
2077 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002078 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002079 flow[1].em.portnr := 20000;
2080 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2081
2082 /* Normal rtp flow for one second */
2083 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2084 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2085 f_sleep(1.0);
2086
2087 /* Now switch the flow over to a new port (BTS) */
2088 port_old := flow[0].em.portnr;
2089 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002090 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002091 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002092 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002093
2094 /* When handing over a call, the old source may still keep
2095 * transmitting for a while. We simulate this by injecting
2096 * some unsolicited packets on the behalf of the old source,
2097 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002098 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2099 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002100 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2101 f_sleep(1.0);
2102 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2103 f_sleep(1.0);
2104
2105 /* Terminate call */
2106 f_flow_delete(RTPEM[0]);
2107 f_flow_delete(RTPEM[1], ep, call_id);
2108
2109 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2110 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2111 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2112 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002113 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002114 }
2115 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2116 if (stats[2].num_pkts_rx_err_disabled != 0) {
2117 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002118 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002119 }
2120
Philipp Maier36291392018-07-25 09:40:44 +02002121 f_rtpem_stats_err_check(stats[0]);
2122 f_rtpem_stats_err_check(stats[1]);
2123 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002124
Philipp Maier2321ef92018-06-27 17:52:04 +02002125 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002126 }
Harald Weltef53f1642017-11-18 19:57:11 +01002127
Philipp Maier6d4e0942019-02-21 17:35:01 +01002128
2129 /* create two local RTP emulations; create two connections on MGW EP, see if
2130 * exchanged data is converted bwtween ts101318 and rfc5993 */
2131 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2132 var RtpFlowData flow[2];
2133 var RtpemStats stats[2];
2134 var MgcpResponse resp;
2135 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2136 var MgcpCallId call_id := '1226'H;
2137
2138 f_init(ep);
2139
2140 /* Turn on conversion mode */
2141 f_vty_enter_config(MGWVTY);
2142 f_vty_transceive(MGWVTY, "mgcp");
2143 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2144
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002145 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002146 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002147 /* bind local RTP emulation sockets */
2148 flow[0].em.portnr := 10000;
2149 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2150 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2151 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2152 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2153 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2154
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002155 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002156 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002157 flow[1].em.portnr := 20000;
2158 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2159 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2160 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2161 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2162 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2163
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002164 /* Send RTP packets to connection #0, receive on connection #1 */
2165 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2166 f_sleep(0.5);
2167 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002168 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002169 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2170 f_sleep(0.5);
2171 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002172
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002173 /* Send RTP packets to connection #1, receive on connection #0 */
2174 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2175 f_sleep(0.5);
2176 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2177 f_sleep(1.0);
2178 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2179 f_sleep(0.5);
2180 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2181
2182 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002183 f_flow_delete(RTPEM[0]);
2184 f_flow_delete(RTPEM[1], ep, call_id);
2185
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002186 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002187 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2188 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002189 f_rtpem_stats_err_check(stats[0]);
2190 f_rtpem_stats_err_check(stats[1]);
2191
2192 /* Turn off conversion mode */
2193 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2194
2195 setverdict(pass);
2196 }
2197
Philipp Maier4f764ce2019-03-07 10:54:10 +01002198 /* create two local RTP emulations; create two connections on MGW EP, see if
2199 * exchanged data is converted between AMR octet-aligned and bandwith
2200 * efficient-mode */
2201 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2202 var RtpFlowData flow[2];
2203 var RtpemStats stats[2];
2204 var MgcpResponse resp;
2205 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2206 var MgcpCallId call_id := '1226'H;
2207
2208 f_init(ep);
2209
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002210 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002211 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002212 /* bind local RTP emulation sockets */
2213 flow[0].em.portnr := 10000;
2214 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2215 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2216 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2217 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2218 flow[0].fmtp := fmtp0;
2219 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2220
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002221 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002222 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002223 flow[1].em.portnr := 20000;
2224 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2225 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2226 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2227 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2228 flow[1].fmtp := fmtp1;
2229 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2230
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002231 /* Send RTP packets to connection #0, receive on connection #1 */
2232 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2233 f_sleep(0.5);
2234 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002235 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002236 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2237 f_sleep(0.5);
2238 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002239
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002240 /* Send RTP packets to connection #1, receive on connection #0 */
2241 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2242 f_sleep(0.5);
2243 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2244 f_sleep(1.0);
2245 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2246 f_sleep(0.5);
2247 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2248
2249 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002250 f_flow_delete(RTPEM[0]);
2251 f_flow_delete(RTPEM[1], ep, call_id);
2252
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002253 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002254 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2255 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002256 f_rtpem_stats_err_check(stats[0]);
2257 f_rtpem_stats_err_check(stats[1]);
2258
2259 setverdict(pass);
2260 }
2261
Philipp Maier882843d2020-05-25 15:33:13 +02002262 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2263 * functions are real world AMR RTP payloads including AMR header. The
2264 * payloads were extracted from a trace with known good payloads. */
2265
Philipp Maier4f764ce2019-03-07 10:54:10 +01002266 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002267 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002268 }
2269
2270 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2271 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2272 }
2273
2274 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2275 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2276 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002277
Harald Weltee636afd2017-09-17 16:24:09 +08002278 /* TODO: Double-DLCX (no retransmission) */
2279
2280
2281
2282 /* TODO: AUEP (various) */
2283 /* TODO: RSIP (various) */
2284 /* TODO: RQNT (various) */
2285 /* TODO: EPCF (various) */
2286 /* TODO: AUCX (various) */
2287 /* TODO: invalid verb (various) */
2288
Oliver Smith021141e2019-06-25 12:09:01 +02002289
2290 testcase TC_conn_timeout() runs on dummy_CT {
2291 var RtpFlowData flow;
2292 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2293 var MgcpCallId call_id := '1225'H;
2294 var MGCP_RecvFrom mrf;
2295
2296 f_init(ep);
2297 log("Setting conn-timeout to 1s");
2298 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2299
2300 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002301 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002302 flow.em.portnr := 10000;
2303 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2304 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2305 f_sleep(1.5);
2306
2307 log("Stopping for 0.5s and resuming");
2308 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2309 f_sleep(0.5);
2310 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2311 f_sleep(0.1);
2312
2313 log("Stopping for 1.5s, expecting to run into timeout");
2314 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2315 f_sleep(1.5);
2316
2317 log("Resuming should fail now");
2318 f_rtpem_conn_refuse_expect(RTPEM[0]);
2319 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2320 f_sleep(0.2);
2321 f_rtpem_conn_refuse_verify(RTPEM[0]);
2322
2323 setverdict(pass);
2324 }
2325
Philipp Maier2609c752020-07-08 12:38:09 +02002326 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2327 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2328 var template MgcpCommand cmd;
2329 var MgcpResponse resp;
2330 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2331 var MgcpCallId call_id := '8376F297'H;
2332
2333 f_init(ep);
2334
2335 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2336 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2337
2338 f_dlcx_ok(ep);
2339
2340 setverdict(pass);
2341 }
2342
2343 /* Test what happens when overlapping endpoints are selected (E1) */
2344 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2345 var template MgcpCommand cmd;
2346 var MgcpResponse resp;
2347 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2348 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2349 var MgcpCallId call_id_1 := '8376F297'H;
2350 var MgcpCallId call_id_2 := '837AF2A7'H;
2351
2352 f_init();
2353
2354 /* ep_1 and ep_2 are overlapping, selecting both one after
2355 * another should work fine: */
2356 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2357 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2358 f_dlcx_ok(ep_1);
2359 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2360 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2361 f_dlcx_ok(ep_2);
2362
2363 /* When ep_1 is serving a call we can not select ep_2 becaus
2364 * it is overlapping with ep_1 */
2365 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2366 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2367 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2368 resp := mgcp_transceive_mgw(cmd, ?);
2369 if (resp.line.code != "501") {
2370 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2371 }
2372 f_dlcx_ok(ep_1);
2373
2374 setverdict(pass);
2375 }
2376
2377 /* Create one connection in loopback mode, test if the RTP packets are
2378 * actually reflected */
2379 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2380 var RtpFlowData flow;
2381 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2382 var MgcpCallId call_id := '12250989'H;
2383 var RtpemStats stats;
2384
2385 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002386 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002387 flow.em.portnr := 10000;
2388 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2389
2390 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2391 f_sleep(1.0);
2392 f_flow_delete(RTPEM[0], ep, call_id);
2393
2394 stats := f_rtpem_stats_get(RTPEM[0]);
2395
2396 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2397 setverdict(fail);
2398 }
2399 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2400 setverdict(fail);
2401 }
2402
2403 f_rtpem_stats_err_check(stats);
2404
2405 setverdict(pass);
2406 }
2407
Philipp Maier13aff992022-06-30 16:19:59 +02002408 /* test valid wildcarded DLCX on an E1 trunk */
2409 testcase TC_e1_dlcx_wildcarded() runs on dummy_CT {
2410 var template MgcpCommand cmd;
2411 var MgcpEndpoint ep;
2412 var MgcpCallId call_id := '8376F297'H;
2413 var integer n_e1_ts := 4;
2414 var StatsDExpects expect;
2415
2416 f_init();
2417
2418 /* Open a few E1 timeslots */
2419 for (var integer i := 0; i < n_e1_ts; i := i+1) {
2420 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-0@" & c_mgw_domain;
2421 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2422 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2423 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-2@" & c_mgw_domain;
2424 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2425 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2426 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-4@" & c_mgw_domain;
2427 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2428 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2429 ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-6@" & c_mgw_domain;
2430 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2431 mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2432 }
2433
2434 /* Wait until the stats items have seteled and then check if we get the expected number (all) of
2435 * occupied endpoints */
2436 f_sleep(1.0)
2437 expect := {
2438 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := n_e1_ts * 4, max := n_e1_ts * 4}
2439 };
2440 f_statsd_expect(expect);
2441
2442 /* Send wildcarded DLCX */
2443 var template MgcpResponse rtmpl := {
2444 line := {
2445 code := "200",
2446 string := ?
2447 },
2448 params:= { },
2449 sdp := omit
2450 };
2451 ep := "ds/e1-1/*@" & c_mgw_domain;
2452 cmd := ts_DLCX(get_next_trans_id(), ep);
2453 mgcp_transceive_mgw(cmd, rtmpl);
2454
2455 /* Query a the statsd once to ensure that intermediate results are pulled from the
2456 * pipeline. The second query (below) will return the actual result. */
2457 expect := {
2458 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := n_e1_ts * 4}
2459 };
2460 f_statsd_expect(expect);
2461
2462 /* The second query must resturn a result with 0 endpoints in use. */
2463 expect := {
2464 { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := 0}
2465 };
2466 f_statsd_expect(expect);
2467
2468 setverdict(pass);
2469 }
2470
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002471 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2472 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2473 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2474 var template MgcpCommand cmd;
2475 var MgcpResponse resp;
2476 var MgcpCallId call_id := '1234'H;
2477 var MgcpConnectionId conn_id;
2478
2479 f_init(ep);
2480
2481 /* create the connection on the MGW */
2482 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2483 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2484 conn_id := extract_conn_id(resp);
2485
2486 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2487 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2488 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2489 valueof(ts_SDP_ptime(20)) });
2490 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2491
2492 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2493 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2494 }
2495 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2496 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2497 }
2498
2499 /* clean-up */
2500 f_dlcx_ok(ep, call_id);
2501 setverdict(pass);
2502 }
2503
2504 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2505 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2506 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2507 var template MgcpCommand cmd;
2508 var MgcpResponse resp;
2509 var MgcpCallId call_id := '1234'H;
2510 var MgcpConnectionId conn_id;
2511
2512 f_init(ep);
2513
2514 /* create the connection on the MGW */
2515 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2516 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2517 conn_id := extract_conn_id(resp);
2518
2519 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2520 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2521 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2522 valueof(ts_SDP_ptime(20)) });
2523 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2524
2525 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2526 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2527 }
2528 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2529 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2530 }
2531
2532 /* clean-up */
2533 f_dlcx_ok(ep, call_id);
2534 setverdict(pass);
2535 }
2536
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002537 /* create two local RTP+IuUP emulations and pass data in both directions */
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002538 function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a,
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002539 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002540 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2541 var RtpFlowData flow[2];
2542 var RtpemStats stats[2];
2543 var MgcpResponse resp;
2544 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2545 var MgcpCallId call_id := '1227'H;
2546 var integer num_pkts_tx[2];
2547 var integer temp;
2548
2549 f_init(ep);
2550
2551 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2552 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2553 flow[0].em.portnr := 10000;
2554 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2555 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2556 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002557 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002558 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002559 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2560 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2561
2562 /* Create the second connection. This connection will be also
2563 * in receive only mode (CN side, IuUP-Init passive) */
2564 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
2565 flow[1].em.portnr := 20000;
2566 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2567 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2568 flow[1].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002569 flow[1].rtp_cfg.iuup_cfg.active_init := false;
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002570 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2571 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2572
2573 /* The first leg starts transmitting */
2574 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2575 f_sleep(0.5);
2576 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2577 if (stats[0].num_pkts_rx_err_disabled != 0) {
2578 setverdict(fail, "received packets from MGW on recvonly connection 0");
2579 mtc.stop;
2580 }
2581 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2582 if (stats[1].num_pkts_rx_err_disabled != 0) {
2583 setverdict(fail, "received packets from MGW on recvonly connection 1");
2584 mtc.stop;
2585 }
2586
2587 /* The second leg starts transmitting a little later */
2588 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2589 f_sleep(1.0);
2590 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2591 if (stats[0].num_pkts_rx_err_disabled != 0) {
2592 setverdict(fail, "received packets from MGW on recvonly connection 0");
2593 mtc.stop;
2594 }
2595 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2596 if (stats[1].num_pkts_rx_err_disabled != 0) {
2597 setverdict(fail, "received packets from MGW on recvonly connection 1");
2598 mtc.stop;
2599 }
2600
2601 /* The first leg will now be switched into bidirectional
2602 * mode, but we do not expect any data coming back yet. */
2603 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2604 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2605 num_pkts_tx[1] := stats[1].num_pkts_tx;
2606 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2607 f_sleep(0.5);
2608 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2609 if (stats[0].num_pkts_rx_err_disabled != 0) {
2610 setverdict(fail, "received packets from MGW on recvonly connection 0");
2611 mtc.stop;
2612 }
2613 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2614 if (stats[1].num_pkts_rx_err_disabled != 0) {
2615 setverdict(fail, "received packets from MGW on recvonly connection 1");
2616 mtc.stop;
2617 }
2618
2619 /* When the second leg is switched into bidirectional mode
2620 * as well, then the MGW will connect the two together and
2621 * we should see RTP streams passing through from both ends. */
2622 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2623 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2624 num_pkts_tx[0] := stats[0].num_pkts_tx;
2625 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2626 f_sleep(2.0);
2627
2628 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2629 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2630
2631 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2632 if (temp > 3 or temp < -3) {
2633 setverdict(fail, "number of packets not within normal parameters:", temp);
2634 mtc.stop;
2635 }
2636
2637 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2638 if (temp > 3 or temp < -3) {
2639 setverdict(fail, "number of packets not within normal parameters:", temp);
2640 mtc.stop;
2641 }
2642
2643 f_rtpem_stats_err_check(stats[0]);
2644 f_rtpem_stats_err_check(stats[1]);
2645
2646 /* Tear down */
2647 f_flow_delete(RTPEM[0]);
2648 f_flow_delete(RTPEM[1], ep, call_id);
2649 setverdict(pass);
2650 }
2651 testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002652 var template (value) IuUP_RabFlowCombinationList rfcl := {
2653 t_IuUP_RFC_AMR_12_2(0),
2654 t_IuUP_RFC_AMR_SID(1),
2655 t_IuUP_RFC_AMR_NO_DATA(2)
2656 };
2657 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2658 mp_local_ipv4, mp_remote_ipv4);
2659 }
2660 /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */
2661 testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT {
2662 var template (value) IuUP_RabFlowCombinationList rfcl := {
2663 t_IuUP_RFC_AMR_12_2(1),
2664 t_IuUP_RFC_AMR_SID(2),
2665 t_IuUP_RFC_AMR_NO_DATA(0)
2666 };
2667 f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002668 mp_local_ipv4, mp_remote_ipv4);
2669 }
2670
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002671 /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */
2672 function f_tc_two_crcx_mdcx_and_iuup_rtp(charstring local_ip_a, charstring remote_ip_a,
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002673 IuUP_RabFlowCombinationList rfcl_a,
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002674 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
2675 var RtpFlowData flow[2];
2676 var RtpemStats stats[2];
2677 var MgcpResponse resp;
2678 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2679 var MgcpCallId call_id := '1227'H;
2680 var integer num_pkts_tx[2];
2681 var integer temp;
2682
2683 f_init(ep);
2684
2685 /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */
2686 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
2687 flow[0].em.portnr := 10000;
2688 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2689 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2690 flow[0].rtp_cfg.tx_fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
2691 flow[0].rtp_cfg.rx_fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O; /* flow[1].rtp_cfg.tx_fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
2692 flow[0].rtp_cfg.iuup_mode := true;
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +02002693 flow[0].rtp_cfg.iuup_cfg.active_init := true;
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002694 flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002695 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
2696 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2697
2698 /* Create the second connection. This connection will be also
2699 * in receive only mode (CN side, regular RTP) */
2700 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
2701 flow[1].em.portnr := 20000;
2702 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2703 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2704 flow[1].rtp_cfg.tx_fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
2705 flow[1].rtp_cfg.rx_fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O; /* flow[0].rtp_cfg.tx_fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
2706 flow[1].rtp_cfg.iuup_mode := false;
2707 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
2708 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2709
2710 /* The first leg starts transmitting */
2711 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
2712 f_sleep(0.5);
2713 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2714 if (stats[0].num_pkts_rx_err_disabled != 0) {
2715 setverdict(fail, "received packets from MGW on recvonly connection 0");
2716 mtc.stop;
2717 }
2718 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2719 if (stats[1].num_pkts_rx_err_disabled != 0) {
2720 setverdict(fail, "received packets from MGW on recvonly connection 1");
2721 mtc.stop;
2722 }
2723
2724 /* The second leg starts transmitting a little later */
2725 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2726 f_sleep(1.0);
2727 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2728 if (stats[0].num_pkts_rx_err_disabled != 0) {
2729 setverdict(fail, "received packets from MGW on recvonly connection 0");
2730 mtc.stop;
2731 }
2732 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2733 if (stats[1].num_pkts_rx_err_disabled != 0) {
2734 setverdict(fail, "received packets from MGW on recvonly connection 1");
2735 mtc.stop;
2736 }
2737
2738 /* The first leg will now be switched into bidirectional
2739 * mode, but we do not expect any data coming back yet. */
2740 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2741 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2742 num_pkts_tx[1] := stats[1].num_pkts_tx;
2743 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2744 f_sleep(0.5);
2745 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2746 if (stats[0].num_pkts_rx_err_disabled != 0) {
2747 setverdict(fail, "received packets from MGW on recvonly connection 0");
2748 mtc.stop;
2749 }
2750 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2751 if (stats[1].num_pkts_rx_err_disabled != 0) {
2752 setverdict(fail, "received packets from MGW on recvonly connection 1");
2753 mtc.stop;
2754 }
2755
2756 /* When the second leg is switched into bidirectional mode
2757 * as well, then the MGW will connect the two together and
2758 * we should see RTP streams passing through from both ends. */
2759 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2760 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2761 num_pkts_tx[0] := stats[0].num_pkts_tx;
2762 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2763 f_sleep(2.0);
2764
2765 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2766 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2767
2768 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
2769 if (temp > 3 or temp < -3) {
2770 setverdict(fail, "number of packets not within normal parameters:", temp);
2771 mtc.stop;
2772 }
2773
2774 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
2775 if (temp > 3 or temp < -3) {
2776 setverdict(fail, "number of packets not within normal parameters:", temp);
2777 mtc.stop;
2778 }
2779
2780 f_rtpem_stats_err_check(stats[0]);
2781 f_rtpem_stats_err_check(stats[1]);
2782
2783 /* Tear down */
2784 f_flow_delete(RTPEM[0]);
2785 f_flow_delete(RTPEM[1], ep, call_id);
2786 setverdict(pass);
2787 }
2788 testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT {
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002789 var template (value) IuUP_RabFlowCombinationList rfcl := {
2790 t_IuUP_RFC_AMR_12_2(0),
2791 t_IuUP_RFC_AMR_SID(1),
2792 t_IuUP_RFC_AMR_NO_DATA(2)
2793 };
2794 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
2795 mp_local_ipv4, mp_remote_ipv4);
2796 }
2797 /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */
2798 testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT {
2799 var template (value) IuUP_RabFlowCombinationList rfcl := {
2800 t_IuUP_RFC_AMR_12_2(1),
2801 t_IuUP_RFC_AMR_SID(2),
2802 t_IuUP_RFC_AMR_NO_DATA(0)
2803 };
2804 f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl),
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002805 mp_local_ipv4, mp_remote_ipv4);
2806 }
2807
Harald Welte00a067f2017-09-13 23:27:17 +02002808 control {
2809 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002810 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002811 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002812 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002813 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002814 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002815 execute(TC_crcx_early_bidir_mode());
2816 execute(TC_crcx_unsupp_param());
2817 execute(TC_crcx_missing_callid());
2818 execute(TC_crcx_missing_mode());
2819 execute(TC_crcx_unsupp_packet_intv());
2820 execute(TC_crcx_illegal_double_lco());
2821 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002822 execute(TC_crcx_wildcarded());
2823 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002824 execute(TC_mdcx_without_crcx());
2825 execute(TC_dlcx_without_crcx());
Philipp Maier21c1cff2021-07-20 14:22:53 +02002826 execute(TC_dlcx_non_existant_ep());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002827 execute(TC_mdcx_wildcarded());
2828 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002829 execute(TC_crcx_and_dlcx_ep_callid_connid());
2830 execute(TC_crcx_and_dlcx_ep_callid());
2831 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002832 execute(TC_crcx_and_dlcx_ep_callid_inval());
2833 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002834 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002835
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002836 execute(TC_crcx_osmux_wildcard());
2837 execute(TC_crcx_osmux_fixed());
2838 execute(TC_crcx_osmux_fixed_twice());
2839 execute(TC_one_crcx_receive_only_osmux());
2840 execute(TC_one_crcx_loopback_osmux());
2841 execute(TC_two_crcx_and_rtp_osmux());
2842 execute(TC_two_crcx_and_rtp_osmux_bidir());
2843 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2844 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2845
Harald Welte33d82162017-12-28 03:21:57 +01002846 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002847
2848 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002849
2850 execute(TC_one_crcx_receive_only_rtp());
2851 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002852 execute(TC_one_crcx_loopback_rtp_ipv6());
Harald Weltebb7523b2018-03-29 08:52:01 +02002853 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002854 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002855 execute(TC_two_crcx_diff_pt_and_rtp());
2856 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002857 execute(TC_two_crcx_mdcx_and_rtp());
2858 execute(TC_two_crcx_and_unsolicited_rtp());
2859 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002860 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002861 execute(TC_amr_oa_bwe_rtp_conversion());
2862 execute(TC_amr_oa_oa_rtp_conversion());
2863 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002864
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002865 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002866
2867 execute(TC_e1_crcx_and_dlcx_ep());
2868 execute(TC_e1_crcx_with_overlap());
2869 execute(TC_e1_crcx_loopback());
Philipp Maier13aff992022-06-30 16:19:59 +02002870 execute(TC_e1_dlcx_wildcarded());
Philipp Maier2609c752020-07-08 12:38:09 +02002871
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002872 execute(TC_crcx_mdcx_ip4());
2873 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002874 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2875 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2876 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2877 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2878 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2879 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2880 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2881 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Philipp Maier37965082021-05-25 16:44:25 +02002882
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002883 execute(TC_two_crcx_mdcx_and_iuup());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002884 execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered());
Pau Espin Pedrol2803cd32022-01-10 18:44:32 +01002885 execute(TC_two_crcx_mdcx_and_iuup_rtp());
Pau Espin Pedrold7963bb2022-05-25 18:19:52 +02002886 execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered());
Pau Espin Pedrola6b9c4c2021-12-24 13:50:26 +01002887
Philipp Maier37965082021-05-25 16:44:25 +02002888 /* Note: This testcase will trigger an OSMO_ASSERT() bug in
2889 * older versions of osmo-mgw. This eventually leads into
2890 * a failure of all subsequent testcases, so it is important
2891 * not to add new testcaes after this one. */
2892 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Welte00a067f2017-09-13 23:27:17 +02002893 }
2894}