blob: c1e263a30a599f0e229894889620f11a30817615 [file] [log] [blame]
Harald Welte34b5a952019-05-27 11:54:11 +02001/* MGW (Media Gateway) test suite in TTCN-3
2 * (C) 2017-2018 Harald Welte <laforge@gnumonks.org>
3 * (C) 2018-2019 sysmocom - s.f.m.c. GmbH
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
Harald Welte00a067f2017-09-13 23:27:17 +020012module MGCP_Test {
Harald Weltef07c2862017-11-18 17:16:24 +010013 import from Osmocom_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +020014 import from MGCP_Types all;
Harald Welte4029e8c2017-11-23 22:00:42 +010015 import from MGCP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080016 import from SDP_Types all;
17 import from MGCP_CodecPort all;
18 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +010019 import from RTP_CodecPort all;
20 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020021 import from RTP_Emulation all;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020022 import from OSMUX_Types all;
23 import from OSMUX_CodecPort all;
24 import from OSMUX_CodecPort_CtrlFunct all;
25 import from OSMUX_Emulation all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080026 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010027 import from General_Types all;
28 import from Native_Functions all;
29 import from IPCP_Types all;
30 import from IP_Types all;
31 import from Osmocom_VTY_Functions all;
32 import from TELNETasp_PortType all;
Harald Welte00a067f2017-09-13 23:27:17 +020033
Philipp Maierbb7a01c2018-02-01 12:32:57 +010034 const charstring c_mgw_domain := "mgw";
35 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
36
Harald Welte21ba5572017-09-19 17:55:05 +080037 /* any variables declared in the component will be available to
38 * all functions that 'run on' the named component, similar to
39 * class members in C++ */
Harald Welte00a067f2017-09-13 23:27:17 +020040 type component dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080041 port MGCP_CODEC_PT MGCP;
42 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010043 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080044 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010045
Philipp Maier2321ef92018-06-27 17:52:04 +020046 var RTP_Emulation_CT vc_RTPEM[3];
47 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010048
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020049 var OSMUX_Emulation_CT vc_OsmuxEM;
50 port OsmuxEM_CTRL_PT OsmuxEM;
51
Philipp Maier6137c322019-02-20 16:13:41 +010052 port TELNETasp_PT MGWVTY;
Harald Welte00a067f2017-09-13 23:27:17 +020053 };
54
Harald Weltee1e18c52017-09-17 16:23:07 +080055 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
56 var MgcpTransId tid := int2str(g_trans_id);
57 g_trans_id := g_trans_id + 1;
58 return tid;
59 }
60
Harald Welte21ba5572017-09-19 17:55:05 +080061 /* all parameters declared here can be modified / overridden by
62 * the config file in the [MODULE_PARAMETERS] section. If no
63 * config file is used or the file doesn't specify them, the
64 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080065 modulepar {
66 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020067 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020068 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080069 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020070 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020071 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010072 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020073 PortNumber mp_local_osmux_port := 1985;
Harald Welte3c6ebb92017-09-16 00:56:57 +080074 }
75
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020076 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
77 /* Turn on conversion mode */
78 f_vty_enter_config(MGWVTY);
79 f_vty_transceive(MGWVTY, "mgcp");
80 if (osmux_on) {
81 f_vty_transceive(MGWVTY, "osmux on");
82 } else {
83 f_vty_transceive(MGWVTY, "osmux off");
84 }
85 f_vty_transceive(MGWVTY, "exit");
86 f_vty_transceive(MGWVTY, "exit");
87
88 }
89
90 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +010091 map(self:MGWVTY, system:MGWVTY);
92 f_vty_set_prompts(MGWVTY);
93 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020094
95 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +010096 }
97
Harald Weltebb7523b2018-03-29 08:52:01 +020098 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
99 runs on dummy_CT {
100 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
101 map(comp_ref:RTP, system:RTP);
102 map(comp_ref:RTCP, system:RTCP);
103 comp_ref.start(RTP_Emulation.f_main());
104 }
105
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200106 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
107 runs on dummy_CT {
108 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
109 map(comp_ref:OSMUX, system:OSMUX);
110 comp_ref.start(OSMUX_Emulation.f_main());
111 }
112
Harald Welte21ba5572017-09-19 17:55:05 +0800113 /* initialization function, called by each test case at the
114 * beginning, but 'initialized' variable ensures its body is
115 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200116 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800117 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100118 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200119
Harald Welteedc45c12017-11-18 19:15:05 +0100120 if (initialized == false) {
121 initialized := true;
122
123 /* some random number for the initial transaction id */
124 g_trans_id := float2int(rnd()*65535.0);
125 map(self:MGCP, system:MGCP_CODEC_PT);
126 /* connect the MGCP test port using the given
127 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
128 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200129 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 +0200130 if (not ispresent(res.connId)) {
131 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200132 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200133 }
Harald Welteedc45c12017-11-18 19:15:05 +0100134 g_mgcp_conn_id := res.connId;
135
Harald Weltebb7523b2018-03-29 08:52:01 +0200136 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
137 f_rtpem_init(vc_RTPEM[i], i);
138 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
139 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200140 if (osmux_on) {
141 f_osmuxem_init(vc_OsmuxEM);
142 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
143 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800144 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800145
Harald Welteedc45c12017-11-18 19:15:05 +0100146 if (isvalue(ep)) {
147 /* do a DLCX on all connections of the EP */
148 f_dlcx_ignore(valueof(ep));
149 }
Philipp Maier6137c322019-02-20 16:13:41 +0100150
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200151 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800152 }
153
Harald Welte00a067f2017-09-13 23:27:17 +0200154 testcase TC_selftest() runs on dummy_CT {
155 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 +0100156 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 +0200157 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
158 "I: 1\n" &
159 "\n" &
160 "v=0\r\n" &
161 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
162 "s=-\r\n" &
163 "c=IN IP4 0.0.0.0\r\n" &
164 "t=0 0\r\n" &
165 "m=audio 0 RTP/AVP 126\r\n" &
166 "a=rtpmap:126 AMR/8000\r\n" &
167 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100168 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 +0200169 "M: sendrecv\r" &
170 "C: 2\r\n" &
171 "I: 1\r\n" &
172 "L: p:20, a:AMR, nt:IN\r\n" &
173 "\n" &
174 "v=0\r\n" &
175 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800176 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200177 "c=IN IP4 0.0.0.0\r\n" &
178 "t=0 0\r\n" &
179 "m=audio 4441 RTP/AVP 99\r\n" &
180 "a=rtpmap:99 AMR/8000\r\n" &
181 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800182 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200183
184 log(c_auep);
185 log(dec_MgcpCommand(c_auep));
186
187 log(c_mdcx3);
188 log(dec_MgcpCommand(c_mdcx3));
189
190 log(c_mdcx3_ret);
191 log(dec_MgcpResponse(c_mdcx3_ret));
192
193 log(c_mdcx4);
194 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800195
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100196 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
197 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 +0800198
199 log(c_crcx510_ret);
200 log(dec_MgcpResponse(c_crcx510_ret));
201 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100202
203 /* We didn't encounter any DTE, so pass the test */
204 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800205 }
206
Harald Weltee636afd2017-09-17 16:24:09 +0800207 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100208 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800209 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
210 * - CRCX with remote session description and without
211 *
212 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100213 * x packetization != 20ms
214 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800215 * x unsupported mode (517)
216 * x bidirectional mode before RemoteConnDesc: 527
217 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100218 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800219 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
220 */
221
Harald Welte21ba5572017-09-19 17:55:05 +0800222 /* build a receive template for receiving a MGCP message. You
223 * pass the MGCP response template in, and it will generate an
224 * MGCP_RecvFrom template that can match the primitives arriving on the
225 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800226 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
227 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100228 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200229 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800230 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200231 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800232 locPort := mp_local_udp_port,
233 msg := { response := resp }
234 }
235 return mrf;
236 }
237
238 /* Send a MGCP request + receive a (matching!) response */
239 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
240 var MgcpMessage msg := { command := valueof(cmd) };
241 resp.line.trans_id := cmd.line.trans_id;
242 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800243 var MGCP_RecvFrom mrf;
244 timer T := 5.0;
245
Harald Welte55015362017-11-18 16:02:42 +0100246 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800247 T.start;
248 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800249 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200250 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
251 setverdict(fail, "Response didn't match template");
252 mtc.stop;
253 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800254 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200255 [] T.timeout {
256 setverdict(fail, "Timeout waiting for response to ", cmd);
257 mtc.stop;
258 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800259 }
260 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800261
262 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
263 return mrf.msg.response;
264 } else {
265 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
266 return r;
267 }
Harald Welte00a067f2017-09-13 23:27:17 +0200268 }
269
Harald Welteba62c8c2017-11-18 18:26:49 +0100270 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
271 var integer i;
272 for (i := 0; i < lengthof(resp.params); i := i + 1) {
273 var MgcpParameter par := resp.params[i];
274 if (par.code == "I") {
275 return str2hex(par.val);
276 }
277 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200278 setverdict(fail, "Could not find conn id for MgcpReponse");
279 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100280 return '00000000'H;
281 }
282
Harald Welte10889c12017-11-18 19:40:31 +0100283 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
284 template MgcpCallId call_id := omit,
285 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100286 var template MgcpCommand cmd;
287 var MgcpResponse resp;
288 var template MgcpResponse rtmpl := {
289 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100290 code := ret_code,
291 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100292 },
293 params := *,
294 sdp := *
295 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100296 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100297 resp := mgcp_transceive_mgw(cmd, rtmpl);
298 }
299
Harald Welte10889c12017-11-18 19:40:31 +0100300 /* Send DLCX and expect OK response */
301 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
302 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100303 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100304 }
305
Harald Welteba62c8c2017-11-18 18:26:49 +0100306 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100307 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100308 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100309 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100310 }
311
Harald Weltebb7523b2018-03-29 08:52:01 +0200312 type record HostPort {
313 charstring hostname,
314 integer portnr optional
315 }
316 type record RtpFlowData {
317 HostPort em, /* emulation side */
318 HostPort mgw, /* mgw side */
319 uint7_t pt,
320 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200321 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100322 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200323 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
324 MgcpOsmuxCID osmux_cid optional,
325 MgcpOsmuxCID osmux_cid_response optional,
326 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100327 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200328 }
329
Philipp Maier2321ef92018-06-27 17:52:04 +0200330 /* Create an RTP flow (bidirectional, or receive-only) */
331 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 +0200332 boolean one_phase := true)
333 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200334 var template MgcpCommand cmd;
335 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100336 var SDP_attribute_list attributes;
337
338 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
339 if (isvalue(flow.fmtp)) {
340 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
341 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200342
343 /* bind local RTP emulation socket */
344 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
345
Philipp Maier28bb8292018-07-20 17:09:17 +0200346 /* configure rtp-emulation */
347 if (ispresent(flow.rtp_cfg)) {
348 f_rtpem_configure(pt, flow.rtp_cfg);
349 } else {
350 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
351 rtp_cfg.tx_payload_type := flow.pt
352 f_rtpem_configure(pt, rtp_cfg);
353 }
354
Harald Weltebb7523b2018-03-29 08:52:01 +0200355 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200356 /* Connect flow to MGW using a CRCX that also contains an SDP
357 * part that tells the MGW where we are listening for RTP streams
358 * that come from the MGW. We get a fully working connection in
359 * one go. */
360
361 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200362 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100363 flow.em.portnr, { int2str(flow.pt) }, attributes);
364
Harald Weltebb7523b2018-03-29 08:52:01 +0200365 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
366 flow.mgcp_conn_id := extract_conn_id(resp);
367 /* extract port number from response */
368 flow.mgw.portnr :=
369 resp.sdp.media_list[0].media_field.ports.port_number;
370 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200371 /* Create a half-open connection only. We do not tell the MGW
372 * where it can send RTP streams to us. This means this
373 * connection will only be able to receive but can not send
374 * data back to us. In order to turn the connection in a fully
375 * bi-directional one, a separate MDCX is needed. */
376
377 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200378 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
379 flow.mgcp_conn_id := extract_conn_id(resp);
380 /* extract MGW-side port number from response */
381 flow.mgw.portnr :=
382 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200383 }
384 /* finally, connect the emulation-side RTP socket to the MGW */
385 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
386 }
387
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200388 /* Create an Osmux flow (bidirectional, or receive-only) */
389 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
390 boolean one_phase := true)
391 runs on dummy_CT {
392 var template MgcpCommand cmd;
393 var MgcpResponse resp;
394 var SDP_attribute_list attributes;
395 var OsmuxTxHandle tx_hdl;
396 var OsmuxRxHandle rx_hdl;
397 var charstring cid_response;
398 var OsmuxCID cid_resp_parsed
399
400 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
401 if (isvalue(flow.fmtp)) {
402 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
403 }
404
405 /* bind local Osmux emulation socket */
406 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
407
408 /* configure osmux-emulation */
409 if (ispresent(flow.osmux_cfg)) {
410 f_osmuxem_configure(pt, flow.osmux_cfg);
411 } else {
412 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
413 f_osmuxem_configure(pt, osmux_cfg);
414 flow.osmux_cfg := osmux_cfg
415 }
416
417 if (one_phase) {
418 /* Connect flow to MGW using a CRCX that also contains an SDP
419 * part that tells the MGW where we are listening for Osmux streams
420 * that come from the MGW. We get a fully working connection in
421 * one go. */
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200422 if (flow.osmux_cid != -1) {
423 /* We may still want to negotiate osmux CID later at MDCX */
424 rx_hdl := c_OsmuxemDefaultRxHandle;
425 rx_hdl.cid := flow.osmux_cid;
426 f_osmuxem_register_rxhandle(pt, rx_hdl);
427 flow.osmux_cid_sent := true;
428 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200429 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
430 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
431 flow.em.portnr, { int2str(flow.pt) }, attributes);
432 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
433 flow.mgcp_conn_id := extract_conn_id(resp);
434 /* extract port number from response */
435 flow.mgw.portnr :=
436 resp.sdp.media_list[0].media_field.ports.port_number;
437 } else {
438 /* Create a half-open connection only. We do not tell the MGW
439 * where it can send Osmux streams to us. This means this
440 * connection will only be able to receive but can not send
441 * data back to us. In order to turn the connection in a fully
442 * bi-directional one, a separate MDCX is needed. */
443
444 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
445 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
446
447 flow.mgcp_conn_id := extract_conn_id(resp);
448 /* extract MGW-side port number from response */
449 flow.mgw.portnr :=
450 resp.sdp.media_list[0].media_field.ports.port_number;
451 }
452
453 /* extract Osmux CID we got assigned by the MGW */
454 var MgcpMessage resp_msg := {
455 response := resp
456 }
457
458 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
459 setverdict(fail, "No Osmux CID in MGCP response", resp);
460 mtc.stop;
461 }
462
463 /* Make sure response is no wildcard */
464 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
465 if (flow.osmux_cid_response == -1) {
466 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
467 mtc.stop;
468 }
469 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
470 f_osmuxem_register_txhandle(pt, tx_hdl);
471
472 /* finally, connect the emulation-side RTP socket to the MGW */
473 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
474 }
475
Philipp Maier2321ef92018-06-27 17:52:04 +0200476 /* Modify an existing RTP flow */
477 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
478 runs on dummy_CT {
479 var template MgcpCommand cmd;
480 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100481 var SDP_attribute_list attributes;
482
483 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
484 if (isvalue(flow.fmtp)) {
485 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
486 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200487
488 /* rebind local RTP emulation socket to the new address */
489 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
490
Philipp Maier28bb8292018-07-20 17:09:17 +0200491 /* reconfigure rtp-emulation */
492 if (ispresent(flow.rtp_cfg)) {
493 f_rtpem_configure(pt, flow.rtp_cfg);
494 } else {
495 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
496 rtp_cfg.tx_payload_type := flow.pt
497 f_rtpem_configure(pt, rtp_cfg);
498 }
499
Philipp Maier2321ef92018-06-27 17:52:04 +0200500 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
501 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
502 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100503 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200504 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
505
506 /* extract MGW-side port number from response. (usually this
507 * will not change, but thats is up to the MGW) */
508 flow.mgw.portnr :=
509 resp.sdp.media_list[0].media_field.ports.port_number;
510
511 /* reconnect the emulation-side RTP socket to the MGW */
512 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
513 }
514
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200515 /* Modify an existing Osmux flow */
516 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
517 runs on dummy_CT {
518 var template MgcpCommand cmd;
519 var MgcpResponse resp;
520 var SDP_attribute_list attributes;
521 var OsmuxRxHandle rx_hdl;
522 var charstring cid_response;
523 var OsmuxCID cid_resp_parsed
524
525 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
526 if (isvalue(flow.fmtp)) {
527 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
528 }
529
530 /* rebind local Osmux emulation socket to the new address */
531 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
532
533 /* configure osmux-emulation */
534 if (ispresent(flow.osmux_cfg)) {
535 f_osmuxem_configure(pt, flow.osmux_cfg);
536 } else {
537 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
538 f_osmuxem_configure(pt, osmux_cfg);
539 }
540
541 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
542 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
543 rx_hdl := c_OsmuxemDefaultRxHandle;
544 rx_hdl.cid := flow.osmux_cid;
545 f_osmuxem_register_rxhandle(pt, rx_hdl);
546 flow.osmux_cid_sent := true;
547 }
548
549 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
550 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
551 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
552 flow.em.portnr, { int2str(flow.pt) }, attributes);
553 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
554
555 /* extract MGW-side port number from response. (usually this
556 * will not change, but thats is up to the MGW) */
557 flow.mgw.portnr :=
558 resp.sdp.media_list[0].media_field.ports.port_number;
559
560 /* extract Osmux CID we got assigned by the MGW */
561 var MgcpMessage resp_msg := {
562 response := resp
563 }
564
565 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
566 setverdict(fail, "No Osmux CID in MGCP response", resp);
567 mtc.stop;
568 }
569
570 /* Make sure response is no wildcard */
571 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
572 if (cid_resp_parsed == -1) {
573 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
574 mtc.stop;
575 }
576 if (cid_resp_parsed != flow.osmux_cid_response) {
577 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
578 mtc.stop;
579 }
580
581 /* reconnect the emulation-side Osmux socket to the MGW */
582 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
583 }
584
Philipp Maier2321ef92018-06-27 17:52:04 +0200585 /* Delete an existing RTP flow */
586 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
587 runs on dummy_CT {
588 var template MgcpCommand cmd;
589 var MgcpResponse resp;
590
591 /* Switch off RTP flow */
592 f_rtpem_mode(pt, RTPEM_MODE_NONE);
593
594 /* Delete connection on MGW (if needed) */
595 if (isvalue(call_id) and isvalue(ep)) {
596 f_sleep(0.1);
597 f_dlcx_ok(valueof(ep), call_id);
598 }
599 }
600
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200601 /* Delete an existing Osmux flow */
602 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
603 runs on dummy_CT {
604 var template MgcpCommand cmd;
605 var MgcpResponse resp;
606
607 /* Switch off Osmux flow */
608 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
609
610 /* Delete connection on MGW (if needed) */
611 if (isvalue(call_id) and isvalue(ep)) {
612 f_sleep(0.1);
613 f_dlcx_ok(valueof(ep), call_id);
614 }
615 }
616
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100617 function f_crcx(charstring ep_prefix) runs on dummy_CT {
618 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800619 var template MgcpCommand cmd;
620 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100621 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800622
Harald Welteedc45c12017-11-18 19:15:05 +0100623 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800624
Harald Welteba62c8c2017-11-18 18:26:49 +0100625 /* create the connection on the MGW */
626 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100627 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100628 extract_conn_id(resp);
629
630 /* clean-up */
631 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100632 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100633
Philipp Maier45635f42018-06-05 17:28:02 +0200634 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
635 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
636 var template MgcpCommand cmd;
637 var MgcpResponse resp;
638 var MgcpCallId call_id := '1234'H;
639
640 f_init(ep);
641
642 /* create the connection on the MGW */
643 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
644 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
645 extract_conn_id(resp);
646
647 /* clean-up */
648 f_dlcx_ok(ep, call_id);
649
650 /* See also OS#2658: Even when we omit the LCO information, we
651 expect the MGW to pick a sane payload type for us. This
652 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200653 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200654 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200655 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200656 }
657
658 /* See also OS#2658: We also expect the MGW to assign a port
659 number to us. */
660 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
661 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200662 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200663 }
664 }
665
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200666 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
667 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
668 var template MgcpCommand cmd;
669 var MgcpResponse resp;
670 var MgcpCallId call_id := '1234'H;
671 var charstring cid_response;
672
673 if (run_init) {
674 f_init(ep, true);
675 }
676
677 /* create the connection on the MGW */
678 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
679 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
680 extract_conn_id(resp);
681
682 /* extract Osmux CID we got assigned by the MGW */
683 var MgcpMessage resp_msg := {
684 response := resp
685 }
686
687 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
688 setverdict(fail, "No Osmux CID in MGCP response", resp);
689 mtc.stop;
690 }
691
692 /* Make sure response is no wildcard */
693 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
694 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
695 mtc.stop;
696 }
697
698 /* clean-up */
699 f_dlcx_ok(ep, call_id);
700 }
701
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100702 /* test valid CRCX without SDP */
703 testcase TC_crcx() runs on dummy_CT {
704 f_crcx(c_mgw_ep_rtpbridge);
705 setverdict(pass);
706 }
707
Philipp Maier45635f42018-06-05 17:28:02 +0200708 /* test valid CRCX without SDP and LCO */
709 testcase TC_crcx_no_lco() runs on dummy_CT {
710 f_crcx_no_lco(c_mgw_ep_rtpbridge);
711 setverdict(pass);
712 }
713
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100714 /* test valid CRCX without SDP (older method without endpoint prefix) */
715 testcase TC_crcx_noprefix() runs on dummy_CT {
716 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800717 setverdict(pass);
718 }
719
720 /* test CRCX with unsupported mode, expect 517 */
721 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
722 var template MgcpCommand cmd;
723 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100724 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100725 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800726 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
727
Harald Welteedc45c12017-11-18 19:15:05 +0100728 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800729
Harald Welteba62c8c2017-11-18 18:26:49 +0100730 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800731 resp := mgcp_transceive_mgw(cmd, rtmpl);
732 setverdict(pass);
733 }
734
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +0200735 /* Test CRCX with X-Osmo-IGN, using same message as SYS#5063 to make sure it doesn't cause a crash. */
736 testcase TC_crcx_osmo_ign() runs on dummy_CT {
737 var template MgcpCommand cmd;
738 var MgcpResponse resp;
739 var MgcpEndpoint ep := "7@" & c_mgw_domain;
740 var MgcpCallId call_id := '3'H;
741
742 f_init(ep);
743
744 /* CRCX 1 7@mgw MGCP 1.0
745 C: 3
746 L: p:20, a:GSM-EFR, nt:IN
747 M: recvonly
748 X-Osmo-IGN: C
749 */
750
751 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
752 cmd.params := {ts_MgcpParCallId(call_id),
753 t_MgcpParLocConnOpt("p:20, a:GSM-EFR, nt:IN"),
754 t_MgcpParConnMode("recvonly"),
755 t_MgcpParOsmoIGN("C")};
756 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
757 extract_conn_id(resp);
758
759 /* clean-up */
760 f_dlcx_ok(ep, call_id);
761 setverdict(pass);
762 }
763
Harald Welte21ba5572017-09-19 17:55:05 +0800764 /* test CRCX with early bi-directional mode, expect 527 as
765 * bi-diretional media can only be established once both local and
766 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800767 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
768 var template MgcpCommand cmd;
769 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100770 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100771 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800772 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
773
Harald Welteedc45c12017-11-18 19:15:05 +0100774 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800775
Harald Welteba62c8c2017-11-18 18:26:49 +0100776 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800777 resp := mgcp_transceive_mgw(cmd, rtmpl);
778 setverdict(pass);
779 }
780
781 /* test CRCX with unsupported Parameters */
782 testcase TC_crcx_unsupp_param() runs on dummy_CT {
783 var template MgcpCommand cmd;
784 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100785 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100786 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800787 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
788
Harald Welteedc45c12017-11-18 19:15:05 +0100789 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800790
Harald Welteba62c8c2017-11-18 18:26:49 +0100791 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100792 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
793 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
794
Harald Weltee636afd2017-09-17 16:24:09 +0800795 resp := mgcp_transceive_mgw(cmd, rtmpl);
796 setverdict(pass);
797 }
798
799 /* test CRCX with missing CallId */
800 testcase TC_crcx_missing_callid() runs on dummy_CT {
801 var template MgcpCommand cmd;
802 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100803 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100804 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800805
Harald Welteedc45c12017-11-18 19:15:05 +0100806 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800807
Harald Welteba62c8c2017-11-18 18:26:49 +0100808 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800809 cmd.params := {
810 t_MgcpParConnMode("recvonly"),
811 t_MgcpParLocConnOpt("p:20")
812 }
813 resp := mgcp_transceive_mgw(cmd, rtmpl);
814 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100815
Harald Weltee636afd2017-09-17 16:24:09 +0800816 }
817
818 /* test CRCX with missing Mode */
819 testcase TC_crcx_missing_mode() runs on dummy_CT {
820 var template MgcpCommand cmd;
821 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100822 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100823 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100824 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
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", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800829 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100830 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800831 t_MgcpParLocConnOpt("p:20")
832 }
833 resp := mgcp_transceive_mgw(cmd, rtmpl);
834 setverdict(pass);
835 }
836
837 /* test CRCX with unsupported packetization interval */
838 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
839 var template MgcpCommand cmd;
840 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100841 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100842 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100843 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800844
Harald Welteedc45c12017-11-18 19:15:05 +0100845 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800846
Harald Welteba62c8c2017-11-18 18:26:49 +0100847 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100848 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800849 resp := mgcp_transceive_mgw(cmd, rtmpl);
850 setverdict(pass);
851 }
852
853 /* test CRCX with illegal double presence of local connection option */
854 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
855 var template MgcpCommand cmd;
856 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100857 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100858 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800859 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
860
Harald Welteedc45c12017-11-18 19:15:05 +0100861 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800862
Harald Welteba62c8c2017-11-18 18:26:49 +0100863 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100864 /* p:20 is permitted only once and not twice! */
865 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800866 resp := mgcp_transceive_mgw(cmd, rtmpl);
867 setverdict(pass);
868 }
869
870 /* test valid CRCX with valid SDP */
871 testcase TC_crcx_sdp() runs on dummy_CT {
872 var template MgcpCommand cmd;
873 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100874 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100875 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800876
Harald Welteedc45c12017-11-18 19:15:05 +0100877 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800878
Harald Welteba62c8c2017-11-18 18:26:49 +0100879 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800880 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
881 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
882 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100883 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100884
885 /* clean-up */
886 f_dlcx_ok(ep, call_id);
887
Harald Weltee636afd2017-09-17 16:24:09 +0800888 setverdict(pass);
889 }
890
Philipp Maier5e06cee2018-02-01 18:28:08 +0100891 /* test valid wildcarded CRCX */
892 testcase TC_crcx_wildcarded() runs on dummy_CT {
893 var template MgcpCommand cmd;
894 var MgcpResponse resp;
895 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
896 var MgcpCallId call_id := '1234'H;
897 var MgcpEndpoint ep_assigned;
898 f_init();
899
900 /* create the connection on the MGW */
901 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
902 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
903 extract_conn_id(resp);
904
905 /* extract endpoint name we got assigned by the MGW */
906 var MgcpMessage resp_msg := {
907 response := resp
908 }
909 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
910 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200911 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100912 }
913
914 /* clean-up */
915 f_dlcx_ok(ep_assigned, call_id);
916
917 setverdict(pass);
918 }
919
920 /* test valid wildcarded CRCX */
921 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maierc60e8472020-07-08 12:57:13 +0200922 const integer n_endpoints := 31;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100923 var integer i;
924 var template MgcpCommand cmd;
925 var MgcpResponse resp;
926 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
927 var MgcpCallId call_id := '1234'H;
928 var MgcpEndpoint ep_assigned[n_endpoints];
929 f_init();
930
931 /* Exhaust all endpoint resources on the virtual trunk */
932 for (i := 0; i < n_endpoints; i := i+1) {
933 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
934 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
935
936 /* Make sure we got a connection id */
937 extract_conn_id(resp);
938
939 var MgcpMessage resp_msg := {
940 response := resp
941 }
942 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
943 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200944 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100945 }
946 }
947
948 /* Try to allocate one more endpoint, which should fail */
949 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
950 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
951 resp := mgcp_transceive_mgw(cmd, rtmpl);
952 setverdict(pass);
953
954 /* clean-up */
955 for (i := 0; i < n_endpoints; i := i+1) {
956 f_dlcx_ok(ep_assigned[i], call_id);
957 }
958 setverdict(pass);
959 }
960
Harald Weltee636afd2017-09-17 16:24:09 +0800961 /* TODO: various SDP related bits */
962
963
964 /* TODO: CRCX with X-Osmux */
965 /* TODO: double CRCX without force_realloc */
966
967 /* TODO: MDCX (various) */
968
969 /* TODO: MDCX without CRCX first */
970 testcase TC_mdcx_without_crcx() runs on dummy_CT {
971 var template MgcpCommand cmd;
972 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100973 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100974 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800975 var template MgcpResponse rtmpl := {
976 line := {
977 /* TODO: accept/enforce better error? */
978 code := "400",
979 string := ?
980 },
981 params:= { },
982 sdp := omit
983 };
984
Harald Welteedc45c12017-11-18 19:15:05 +0100985 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800986
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100987 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800988 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
989 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
990 valueof(ts_SDP_ptime(20)) });
991 resp := mgcp_transceive_mgw(cmd, rtmpl);
992 setverdict(pass);
993 }
994
995 /* DLCX without CRCX first */
996 testcase TC_dlcx_without_crcx() runs on dummy_CT {
997 var template MgcpCommand cmd;
998 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100999 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +08001000 var template MgcpResponse rtmpl := {
1001 line := {
Harald Weltef91edf32017-12-28 14:16:21 +01001002 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +08001003 string := ?
1004 },
1005 params:= { },
1006 sdp := omit
1007 };
1008
Harald Welteedc45c12017-11-18 19:15:05 +01001009 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +08001010
Harald Welteedc45c12017-11-18 19:15:05 +01001011 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +08001012 resp := mgcp_transceive_mgw(cmd, rtmpl);
1013 setverdict(pass);
1014 }
1015
Philipp Maier8a3dc922018-02-02 14:55:12 +01001016 /* test valid wildcarded MDCX */
1017 testcase TC_mdcx_wildcarded() runs on dummy_CT {
1018 /* Note: A wildcarded MDCX is not allowed, so we expect the
1019 * MGW to reject this request */
1020 var template MgcpCommand cmd;
1021 var MgcpResponse resp;
1022 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1023 var MgcpCallId call_id := '1225'H;
1024 var template MgcpResponse rtmpl := {
1025 line := {
1026 /* TODO: accept/enforce better error? */
1027 code := "507",
1028 string := ?
1029 },
1030 params:= { },
1031 sdp := omit
1032 };
1033
1034 f_init(ep);
1035
1036 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1037 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1038 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1039 valueof(ts_SDP_ptime(20)) });
1040 resp := mgcp_transceive_mgw(cmd, rtmpl);
1041 setverdict(pass);
1042 }
1043
1044 /* test valid wildcarded DLCX */
1045 testcase TC_dlcx_wildcarded() runs on dummy_CT {
1046 /* Note: A wildcarded DLCX is specified, but our MGW does not
1047 * support this feature so we expect the MGW to reject the
1048 * request */
1049 var template MgcpCommand cmd;
1050 var MgcpResponse resp;
1051 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1052 var template MgcpResponse rtmpl := {
1053 line := {
1054 code := "507",
1055 string := ?
1056 },
1057 params:= { },
1058 sdp := omit
1059 };
1060
1061 f_init(ep);
1062
1063 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1064 resp := mgcp_transceive_mgw(cmd, rtmpl);
1065 setverdict(pass);
1066 }
1067
Harald Welte79181ff2017-11-18 19:26:11 +01001068 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1069 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1070 var template MgcpCommand cmd;
1071 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001072 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001073 var MgcpCallId call_id := '51234'H;
1074
1075 f_init(ep);
1076
1077 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1078 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1079
1080 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1081
1082 setverdict(pass);
1083 }
1084
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001085 /* test valid CRCX without SDP */
1086 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1087 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1088 setverdict(pass);
1089 }
1090
1091 /* test valid CRCX without SDP */
1092 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1093 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1094 setverdict(pass);
1095 }
1096
1097 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1098 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1099 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1100 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1101 setverdict(pass);
1102 }
1103
1104 /* Create one half open connection in receive-only mode. The MGW must accept
1105 * the packets but must not send any. */
1106 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1107 var RtpFlowData flow;
1108 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1109 var MgcpCallId call_id := '1225'H;
1110 var OsmuxemStats stats;
1111 var OsmuxTxHandle tx_hdl;
1112
1113 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001114 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001115 flow.em.portnr := mp_local_osmux_port;
1116 flow.osmux_cid := -1;
1117 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1118
1119 /* create a transmitter not yet known by MGW */
1120 tx_hdl := valueof(t_TxHandleAMR590(2));
1121 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1122
1123 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1124 f_sleep(1.0);
1125 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1126
1127 stats := f_osmuxem_stats_get(OsmuxEM);
1128
1129 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1130 setverdict(fail);
1131 }
1132 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1133 setverdict(fail);
1134 }
1135
1136 f_osmuxem_stats_err_check(stats);
1137
1138 setverdict(pass);
1139 }
1140
1141 /* Create one connection in loopback mode, test if the Osmux packets are
1142 * actually reflected */
1143 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1144 var RtpFlowData flow;
1145 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1146 var MgcpCallId call_id := '1225'H;
1147 var OsmuxemStats stats;
1148 var OsmuxTxHandle tx_hdl;
1149
1150 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001151 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001152 flow.em.portnr := mp_local_osmux_port;
1153 flow.osmux_cid := 2;
1154 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1155
1156 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1157 f_sleep(1.0);
1158
1159 /* Switch off both Tx, wait to receive delayed frames from MGW */
1160 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1161 f_sleep(0.1);
1162 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1163
1164 stats := f_osmuxem_stats_get(OsmuxEM);
1165
1166 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1167 setverdict(fail);
1168 }
1169 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1170 setverdict(fail);
1171 }
1172
1173 f_osmuxem_stats_err_check(stats);
1174
1175 setverdict(pass);
1176 }
1177
1178 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1179 * must match the reception statistics on the other side and vice versa. The
1180 * user may also supply a tolerance value (number of packets) when deviations
1181 * are acceptable */
1182 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1183 var integer plen;
1184
1185 log("stats A: ", a);
1186 log("stats B: ", b);
1187 log("tolerance: ", tolerance, " packets");
1188 log("batch_size: ", batch_size, " packets");
1189
1190 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1191
1192 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1193 return false;
1194 }
1195
1196 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1197 return false;
1198 }
1199
1200 if(a.num_pkts_tx > 0) {
1201 plen := a.bytes_payload_tx / a.num_pkts_tx;
1202 } else {
1203 plen := 0;
1204 }
1205
1206 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1207 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1208 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1209 return false;
1210 }
1211
1212 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) {
1213 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1214 return false;
1215 }
1216
1217 return true;
1218 }
1219
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001220 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1221 charstring local_ip_rtp, charstring remote_ip_rtp,
1222 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001223 var RtpFlowData flow[2];
1224 var RtpemStats stats_rtp;
1225 var OsmuxemStats stats_osmux;
1226 var MgcpResponse resp;
1227 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1228 var MgcpCallId call_id := '1226'H;
1229 var integer tolerance := 0;
1230
1231 f_init(ep, true);
1232
1233 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001234 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001235 flow[0].rtp_cfg := c_RtpemDefaultCfg
1236 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1237 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1238 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);
1239 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1240 /* bind local RTP emulation sockets */
1241 flow[0].em.portnr := 10000;
1242 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1243
1244 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001245 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001246 flow[1].em.portnr := mp_local_osmux_port;
1247 flow[1].osmux_cid := 2;
1248 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1249 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1250
1251 if (bidir) {
1252 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1253 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1254
1255 /* Note: When we test bidirectional we may
1256 * loose packets during switch off because
1257 * both ends are transmitting and we only
1258 * can switch them off one by one. */
1259 tolerance := 3;
1260 } else {
1261 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1262 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1263 }
1264
1265 f_sleep(1.0);
1266
1267 /* Switch off both Tx, wait to receive delayed frames from MGW */
1268 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1269 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1270 f_sleep(0.1);
1271
1272 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1273 f_flow_delete(RTPEM[1]);
1274
1275 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1276 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1277 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1278 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1279 mtc.stop;
1280 }
1281
1282 f_rtpem_stats_err_check(stats_rtp);
1283 f_osmuxem_stats_err_check(stats_osmux);
1284
1285 setverdict(pass);
1286 }
1287
1288 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1289 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001290 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1291 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001292 }
1293
1294 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1295 * exchange some data in both directions */
1296 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001297 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1298 mp_local_ipv4, mp_remote_ipv4);
1299 }
1300
1301 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1302 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1303 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1304 mp_local_ipv6, mp_remote_ipv6);
1305 }
1306 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1307 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1308 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1309 mp_local_ipv6, mp_remote_ipv6);
1310 }
1311 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1312 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1313 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1314 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001315 }
1316
1317
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001318 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1319 charstring local_ip_rtp, charstring remote_ip_rtp,
1320 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001321 var RtpFlowData flow[2];
1322 var RtpemStats stats_rtp;
1323 var OsmuxemStats stats_osmux;
1324 var MgcpResponse resp;
1325 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1326 var MgcpCallId call_id := '1227'H;
1327 var integer num_pkts_tx[2];
1328 var integer temp;
1329
1330 f_init(ep, true);
1331
1332 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001333 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001334 flow[0].rtp_cfg := c_RtpemDefaultCfg
1335 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1336 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1337 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);
1338 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1339 /* bind local RTP emulation sockets */
1340 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001341 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001342
1343
1344 /* Create the second connection. This connection will be also
1345 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001346 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001347 flow[1].em.portnr := mp_local_osmux_port;
1348 if (crcx_osmux_wildcard) {
1349 flow[1].osmux_cid := -1;
1350 } else {
1351 flow[1].osmux_cid := 2;
1352 }
1353 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001354 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001355
1356
1357 /* The first leg starts transmitting */
1358 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1359 f_sleep(0.5);
1360 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1361 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1362 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1363 mtc.stop;
1364 }
1365 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1366 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1367 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1368 mtc.stop;
1369 }
1370
1371 /* The second leg starts transmitting a little later */
1372 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1373 f_sleep(1.0);
1374 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1375 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1376 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1377 mtc.stop;
1378 }
1379 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1380 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1381 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1382 mtc.stop;
1383 }
1384
1385 /* The first leg will now be switched into bidirectional
1386 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001387 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1388 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1389 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001390 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1391 f_sleep(0.5);
1392 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1393 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1394 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1395 mtc.stop;
1396 }
1397 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1398 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1399 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1400 mtc.stop;
1401 }
1402
1403 /* When the second leg is switched into bidirectional mode
1404 * as well, then the MGW will connect the two together and
1405 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001406 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1407 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001408 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001409
1410 if (crcx_osmux_wildcard) {
1411 /* For now we must set same CID as the MGW recvCID,
1412 * having sendCID!=recvCID is not yet supported. */
1413 flow[1].osmux_cid := flow[1].osmux_cid_response;
1414 }
1415 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1416 f_sleep(2.0);
1417
1418 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1419 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1420
1421 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1422 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1423 log("stats_rtp: ", stats_rtp);
1424 log("stats_osmux: ", stats_osmux);
1425 log("old_rtp_tx: ", num_pkts_tx[0]);
1426 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1427 mtc.stop;
1428 }
1429
1430 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1431 if (temp > 3 or temp < -3) {
1432 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1433 mtc.stop;
1434 }
1435
1436 f_rtpem_stats_err_check(stats_rtp);
1437 f_osmuxem_stats_err_check(stats_osmux);
1438
1439 /* Tear down */
1440 f_flow_delete(RTPEM[0]);
1441 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1442 setverdict(pass);
1443 }
1444
1445 /* create one RTP and one OSmux emulations and pass data in both
1446 directions. Create CRCX with wildcard Osmux CID and set it later
1447 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1448 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001449 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1450 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001451 }
1452
1453 /* create one RTP and one OSmux emulations and pass data in both
1454 directions. Create CRCX with fixed Osmux CID and keep it during
1455 MDCX. This is similar to how BSC sets up the call in AoIP. */
1456 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001457 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1458 mp_local_ipv4, mp_remote_ipv4);
1459 }
1460
1461 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1462 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1463 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1464 mp_local_ipv6, mp_remote_ipv6);
1465 }
1466 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1467 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1468 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1469 mp_local_ipv6, mp_remote_ipv6);
1470 }
1471 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1472 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1473 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1474 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001475 }
1476
Harald Welte646ecdb2017-12-28 03:21:57 +01001477 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1478 var template MgcpCommand cmd;
1479 var MgcpResponse resp;
1480
1481 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1482 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1483
1484 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1485
1486 setverdict(pass);
1487 }
1488
1489 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1490 var MgcpEndpoint ep;
1491 var MgcpCallId call_id;
1492 var integer ep_nr;
1493
1494 f_init();
1495
1496 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001497 if(ep_nr > 15) {
1498 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1499 } else {
1500 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1501 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001502 call_id := int2hex(ep_nr, 2) & '1234'H;
1503 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1504 }
1505 }
1506
Harald Welte79181ff2017-11-18 19:26:11 +01001507 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1508 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001509 var template MgcpCommand cmd;
1510 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001511 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001512 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001513
Harald Welteedc45c12017-11-18 19:15:05 +01001514 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001515
Harald Welteba62c8c2017-11-18 18:26:49 +01001516 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001517 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001518
Harald Welteba62c8c2017-11-18 18:26:49 +01001519 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001520
1521 setverdict(pass);
1522 }
1523
Harald Welte79181ff2017-11-18 19:26:11 +01001524 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1525 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1526 var template MgcpCommand cmd;
1527 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001528 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001529 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001530
1531 f_init(ep);
1532
1533 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1534 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1535
1536 f_dlcx_ok(ep);
1537
1538 setverdict(pass);
1539 }
1540
1541
Harald Welte6d167f82017-11-18 19:41:35 +01001542 /* CRCX + DLCX of valid endpoint but invalid call-id */
1543 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1544 var template MgcpCommand cmd;
1545 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001546 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001547 var MgcpCallId call_id := '51231'H;
1548
1549 f_init(ep);
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(ep, "516", *, 'ffff'H);
1555
1556 setverdict(pass);
1557 }
1558
1559
1560 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1561 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1562 var template MgcpCommand cmd;
1563 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001564 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001565 var MgcpCallId call_id := '51230'H;
1566
1567 f_init(ep);
1568
1569 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1570 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1571
1572 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1573
1574 setverdict(pass);
1575 }
1576
1577
Harald Weltee636afd2017-09-17 16:24:09 +08001578 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001579 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1580 var template MgcpCommand cmd;
1581 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001582 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001583 var MgcpCallId call_id := '51229'H;
1584 var template MgcpResponse rtmpl := {
1585 line := {
1586 code := "200",
1587 string := "OK"
1588 },
1589 params:= { },
1590 sdp := omit
1591 };
1592
1593 f_init(ep);
1594
1595 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1596 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1597
1598 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1599 resp := mgcp_transceive_mgw(cmd, rtmpl);
1600 resp := mgcp_transceive_mgw(cmd, rtmpl);
1601
1602 setverdict(pass);
1603 }
1604
Harald Weltebb7523b2018-03-29 08:52:01 +02001605 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1606 charstring codec) := {
1607 em := {
1608 hostname := host_a,
1609 portnr := omit
1610 },
1611 mgw := {
1612 hostname := host_b,
1613 portnr := omit
1614 },
1615 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001616 codec := codec,
1617 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001618 }
Harald Weltef53f1642017-11-18 19:57:11 +01001619
Harald Weltebb7523b2018-03-29 08:52:01 +02001620 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1621 testcase TC_rtpem_selftest() runs on dummy_CT {
1622 var RtpemStats stats[2];
1623 var integer local_port := 10000;
1624 var integer local_port2 := 20000;
1625
1626 f_init();
1627
1628 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1629 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1630
1631 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1632 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1633
1634 log("=== starting");
1635 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1636 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1637
1638 f_sleep(5.0);
1639
1640 log("=== stopping");
1641 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1642 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1643 f_sleep(0.5);
1644 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1645 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1646
1647 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1648 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1649 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1650 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001651 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001652 }
1653 setverdict(pass);
1654 }
1655
Philipp Maier2321ef92018-06-27 17:52:04 +02001656 /* Create one half open connection in receive-only mode. The MGW must accept
1657 * the packets but must not send any. */
1658 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1659 var RtpFlowData flow;
1660 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1661 var MgcpCallId call_id := '1225'H;
1662 var RtpemStats stats;
1663
1664 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001665 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001666 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001667 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001668
1669 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1670 f_sleep(1.0);
1671 f_flow_delete(RTPEM[0], ep, call_id);
1672
1673 stats := f_rtpem_stats_get(RTPEM[0]);
1674
Philipp Maier42b17cc2019-10-01 13:53:17 +02001675 /* Make sure that at least some amount of RTP packets/bytes
1676 * have has been transmitted. The compare values for
1677 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1678 * using a testrun and the results were devided by 2, so even
1679 * in load situations we should reach the minimum amount of
1680 * required packets/bytes */
1681
1682 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001683 setverdict(fail);
1684 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001685 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001686 setverdict(fail);
1687 }
Philipp Maier36291392018-07-25 09:40:44 +02001688
1689 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001690
1691 setverdict(pass);
1692 }
1693
1694 /* Create one connection in loopback mode, test if the RTP packets are
1695 * actually reflected */
Philipp Maiereba70db2021-05-17 18:31:39 +02001696 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 +02001697 var RtpFlowData flow;
1698 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1699 var MgcpCallId call_id := '1225'H;
1700 var RtpemStats stats;
1701
1702 f_init(ep);
Philipp Maiereba70db2021-05-17 18:31:39 +02001703 flow := valueof(t_RtpFlow(local_ip, remote_ip, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001704 flow.em.portnr := 10000;
Philipp Maier1ac13982021-05-07 23:06:07 +02001705 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase);
Philipp Maier2321ef92018-06-27 17:52:04 +02001706
1707 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1708 f_sleep(1.0);
1709 f_flow_delete(RTPEM[0], ep, call_id);
1710
1711 stats := f_rtpem_stats_get(RTPEM[0]);
1712
1713 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1714 setverdict(fail);
1715 }
1716 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1717 setverdict(fail);
1718 }
Philipp Maier36291392018-07-25 09:40:44 +02001719
1720 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001721
1722 setverdict(pass);
1723 }
1724
Philipp Maier1ac13982021-05-07 23:06:07 +02001725 /* Create one connection in loopback mode, test if the RTP packets are
1726 * actually reflected */
1727 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001728 f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true)
Philipp Maier1ac13982021-05-07 23:06:07 +02001729 }
Philipp Maierc0ca42d2021-05-17 18:41:19 +02001730 testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT {
1731 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true)
1732 }
Philipp Maier1ac13982021-05-07 23:06:07 +02001733
1734 /* Same as above, but we will intenionally not tell the MGW where to
1735 * send the outgoing traffic. The connection is still created in
1736 * loopback mode, so the MGW should take the originating address from
1737 * the incoming RTP packet and send it back to the source */
1738 testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT {
Philipp Maiereba70db2021-05-17 18:31:39 +02001739 f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false)
Philipp Maier1ac13982021-05-07 23:06:07 +02001740 }
1741
1742
Philipp Maier7df85f62018-07-25 10:26:09 +02001743 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1744 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001745 var RtpFlowData flow[2];
1746 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001747 var MgcpResponse resp;
1748 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1749 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001750 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001751
1752 f_init(ep);
1753
1754 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001755 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001756 /* bind local RTP emulation sockets */
1757 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001758 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001759
1760 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001761 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001762 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001763 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1764
1765 if (bidir) {
1766 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1767 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1768
1769 /* Note: When we test bidirectional we may
1770 * loose packets during switch off because
1771 * both ends are transmitting and we only
1772 * can switch them off one by one. */
1773 tolerance := 3;
1774 } else {
1775 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1776 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1777 }
1778
1779 f_sleep(1.0);
1780
1781 f_flow_delete(RTPEM[1]);
1782 f_flow_delete(RTPEM[0], ep, call_id);
1783
1784 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1785 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1786 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1787 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001788 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001789 }
1790
Philipp Maier36291392018-07-25 09:40:44 +02001791 f_rtpem_stats_err_check(stats[0]);
1792 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001793
Philipp Maier2321ef92018-06-27 17:52:04 +02001794 setverdict(pass);
1795 }
1796
1797 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1798 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001799 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001800 }
1801
1802 /* create two local RTP emulations; create two connections on MGW EP,
1803 * exchange some data in both directions */
1804 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001805 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1806 }
1807
1808 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1809 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1810 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1811 }
1812
1813 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1814 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1815 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001816 }
1817
1818 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001819 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1820 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001821 var RtpFlowData flow[2];
1822 var RtpemStats stats[2];
1823 var MgcpResponse resp;
1824 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1825 var MgcpCallId call_id := '1227'H;
1826 var integer num_pkts_tx[2];
1827 var integer temp;
1828
1829 f_init(ep);
1830
1831 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001832 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001833 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001834 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001835
1836 /* Create the second connection. This connection will be also
1837 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001838 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001839 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001840 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001841
1842 /* The first leg starts transmitting */
1843 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1844 f_sleep(0.5);
1845 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1846 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001847 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001848 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001849 }
1850 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1851 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001852 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001853 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001854 }
1855
1856 /* The second leg starts transmitting a little later */
1857 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1858 f_sleep(1.0);
1859 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1860 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001861 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001862 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001863 }
1864 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1865 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001866 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001867 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001868 }
1869
1870 /* The first leg will now be switched into bidirectional
1871 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001872 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1873 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1874 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001875 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1876 f_sleep(0.5);
1877 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001878 if (stats[0].num_pkts_rx_err_disabled != 0) {
1879 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001880 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001881 }
1882 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1883 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001884 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001885 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001886 }
1887
1888 /* When the second leg is switched into bidirectional mode
1889 * as well, then the MGW will connect the two together and
1890 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02001891 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1892 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001893 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001894 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1895 f_sleep(2.0);
1896
1897 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1898 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1899
1900 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1901 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001902 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001903 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001904 }
1905
1906 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1907 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001908 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001909 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001910 }
Philipp Maier36291392018-07-25 09:40:44 +02001911
1912 f_rtpem_stats_err_check(stats[0]);
1913 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001914
1915 /* Tear down */
1916 f_flow_delete(RTPEM[0]);
1917 f_flow_delete(RTPEM[1], ep, call_id);
1918 setverdict(pass);
1919 }
1920
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001921 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1922 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1923 mp_local_ipv4, mp_remote_ipv4);
1924 }
1925
1926 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
1927 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
1928 mp_local_ipv6, mp_remote_ipv6);
1929 }
1930
1931 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
1932 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1933 mp_local_ipv6, mp_remote_ipv6);
1934 }
1935
Philipp Maier2321ef92018-06-27 17:52:04 +02001936 /* Test what happens when two RTP streams from different sources target
1937 * a single connection. Is the unsolicited stream properly ignored? */
1938 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1939 var RtpFlowData flow[2];
1940 var RtpemStats stats[2];
1941 var MgcpResponse resp;
1942 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1943 var MgcpCallId call_id := '1234321326'H;
1944 var integer unsolicited_port := 10002;
1945
1946 f_init(ep);
1947
1948 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001949 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001950 /* bind local RTP emulation sockets */
1951 flow[0].em.portnr := 10000;
1952 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1953
1954 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001955 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001956 flow[1].em.portnr := 20000;
1957 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001958
1959 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1960 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1961
Philipp Maier2321ef92018-06-27 17:52:04 +02001962 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001963
Philipp Maier2321ef92018-06-27 17:52:04 +02001964 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001965 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
1966 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001967 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1968
1969 f_sleep(0.5);
1970
Daniel Willmanna069d382018-12-13 13:53:33 +01001971 /* Stop transmitting packets and tear down the flows */
1972 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02001973 f_flow_delete(RTPEM[0]);
1974 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001975
1976 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1977 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1978 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1979 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001980 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001981 }
1982
Philipp Maier36291392018-07-25 09:40:44 +02001983 f_rtpem_stats_err_check(stats[0]);
1984 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02001985
Harald Weltebb7523b2018-03-29 08:52:01 +02001986 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001987 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001988
Philipp Maier2321ef92018-06-27 17:52:04 +02001989 /* Test a handover situation. We first create two connections transmit
1990 * some data bidirectionally. Then we will simulate a handover situation. */
1991 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1992 var RtpFlowData flow[2];
1993 var RtpemStats stats[3];
1994 var MgcpResponse resp;
1995 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1996 var MgcpCallId call_id := '76338'H;
1997 var integer port_old;
1998
1999 f_init(ep);
2000
2001 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002002 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002003 /* bind local RTP emulation sockets */
2004 flow[0].em.portnr := 10000;
2005 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2006
2007 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002008 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02002009 flow[1].em.portnr := 20000;
2010 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2011
2012 /* Normal rtp flow for one second */
2013 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2014 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2015 f_sleep(1.0);
2016
2017 /* Now switch the flow over to a new port (BTS) */
2018 port_old := flow[0].em.portnr;
2019 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002020 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02002021 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02002022 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02002023
2024 /* When handing over a call, the old source may still keep
2025 * transmitting for a while. We simulate this by injecting
2026 * some unsolicited packets on the behalf of the old source,
2027 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002028 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
2029 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02002030 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
2031 f_sleep(1.0);
2032 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
2033 f_sleep(1.0);
2034
2035 /* Terminate call */
2036 f_flow_delete(RTPEM[0]);
2037 f_flow_delete(RTPEM[1], ep, call_id);
2038
2039 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2040 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2041 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
2042 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02002043 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002044 }
2045 stats[2] := f_rtpem_stats_get(RTPEM[2]);
2046 if (stats[2].num_pkts_rx_err_disabled != 0) {
2047 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002048 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002049 }
2050
Philipp Maier36291392018-07-25 09:40:44 +02002051 f_rtpem_stats_err_check(stats[0]);
2052 f_rtpem_stats_err_check(stats[1]);
2053 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002054
Philipp Maier2321ef92018-06-27 17:52:04 +02002055 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002056 }
Harald Weltef53f1642017-11-18 19:57:11 +01002057
Philipp Maier6d4e0942019-02-21 17:35:01 +01002058
2059 /* create two local RTP emulations; create two connections on MGW EP, see if
2060 * exchanged data is converted bwtween ts101318 and rfc5993 */
2061 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2062 var RtpFlowData flow[2];
2063 var RtpemStats stats[2];
2064 var MgcpResponse resp;
2065 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2066 var MgcpCallId call_id := '1226'H;
2067
2068 f_init(ep);
2069
2070 /* Turn on conversion mode */
2071 f_vty_enter_config(MGWVTY);
2072 f_vty_transceive(MGWVTY, "mgcp");
2073 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2074
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002075 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002076 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002077 /* bind local RTP emulation sockets */
2078 flow[0].em.portnr := 10000;
2079 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2080 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2081 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2082 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2083 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2084
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002085 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002086 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002087 flow[1].em.portnr := 20000;
2088 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2089 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2090 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2091 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2092 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2093
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002094 /* Send RTP packets to connection #0, receive on connection #1 */
2095 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2096 f_sleep(0.5);
2097 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002098 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002099 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2100 f_sleep(0.5);
2101 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002102
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002103 /* Send RTP packets to connection #1, receive on connection #0 */
2104 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2105 f_sleep(0.5);
2106 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2107 f_sleep(1.0);
2108 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2109 f_sleep(0.5);
2110 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2111
2112 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002113 f_flow_delete(RTPEM[0]);
2114 f_flow_delete(RTPEM[1], ep, call_id);
2115
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002116 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002117 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2118 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002119 f_rtpem_stats_err_check(stats[0]);
2120 f_rtpem_stats_err_check(stats[1]);
2121
2122 /* Turn off conversion mode */
2123 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2124
2125 setverdict(pass);
2126 }
2127
Philipp Maier4f764ce2019-03-07 10:54:10 +01002128 /* create two local RTP emulations; create two connections on MGW EP, see if
2129 * exchanged data is converted between AMR octet-aligned and bandwith
2130 * efficient-mode */
2131 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) 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
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002140 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002141 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002142 /* bind local RTP emulation sockets */
2143 flow[0].em.portnr := 10000;
2144 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2145 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2146 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2147 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2148 flow[0].fmtp := fmtp0;
2149 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2150
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002151 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002152 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002153 flow[1].em.portnr := 20000;
2154 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2155 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2156 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2157 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2158 flow[1].fmtp := fmtp1;
2159 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2160
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002161 /* Send RTP packets to connection #0, receive on connection #1 */
2162 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2163 f_sleep(0.5);
2164 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002165 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002166 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2167 f_sleep(0.5);
2168 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002169
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002170 /* Send RTP packets to connection #1, receive on connection #0 */
2171 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2172 f_sleep(0.5);
2173 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2174 f_sleep(1.0);
2175 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2176 f_sleep(0.5);
2177 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2178
2179 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002180 f_flow_delete(RTPEM[0]);
2181 f_flow_delete(RTPEM[1], ep, call_id);
2182
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002183 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002184 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2185 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002186 f_rtpem_stats_err_check(stats[0]);
2187 f_rtpem_stats_err_check(stats[1]);
2188
2189 setverdict(pass);
2190 }
2191
Philipp Maier882843d2020-05-25 15:33:13 +02002192 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2193 * functions are real world AMR RTP payloads including AMR header. The
2194 * payloads were extracted from a trace with known good payloads. */
2195
Philipp Maier4f764ce2019-03-07 10:54:10 +01002196 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002197 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002198 }
2199
2200 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2201 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2202 }
2203
2204 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2205 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2206 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002207
Harald Weltee636afd2017-09-17 16:24:09 +08002208 /* TODO: Double-DLCX (no retransmission) */
2209
2210
2211
2212 /* TODO: AUEP (various) */
2213 /* TODO: RSIP (various) */
2214 /* TODO: RQNT (various) */
2215 /* TODO: EPCF (various) */
2216 /* TODO: AUCX (various) */
2217 /* TODO: invalid verb (various) */
2218
Oliver Smith021141e2019-06-25 12:09:01 +02002219
2220 testcase TC_conn_timeout() runs on dummy_CT {
2221 var RtpFlowData flow;
2222 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2223 var MgcpCallId call_id := '1225'H;
2224 var MGCP_RecvFrom mrf;
2225
2226 f_init(ep);
2227 log("Setting conn-timeout to 1s");
2228 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2229
2230 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002231 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002232 flow.em.portnr := 10000;
2233 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2234 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2235 f_sleep(1.5);
2236
2237 log("Stopping for 0.5s and resuming");
2238 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2239 f_sleep(0.5);
2240 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2241 f_sleep(0.1);
2242
2243 log("Stopping for 1.5s, expecting to run into timeout");
2244 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2245 f_sleep(1.5);
2246
2247 log("Resuming should fail now");
2248 f_rtpem_conn_refuse_expect(RTPEM[0]);
2249 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2250 f_sleep(0.2);
2251 f_rtpem_conn_refuse_verify(RTPEM[0]);
2252
2253 setverdict(pass);
2254 }
2255
Philipp Maier2609c752020-07-08 12:38:09 +02002256 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2257 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2258 var template MgcpCommand cmd;
2259 var MgcpResponse resp;
2260 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2261 var MgcpCallId call_id := '8376F297'H;
2262
2263 f_init(ep);
2264
2265 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2266 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2267
2268 f_dlcx_ok(ep);
2269
2270 setverdict(pass);
2271 }
2272
2273 /* Test what happens when overlapping endpoints are selected (E1) */
2274 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2275 var template MgcpCommand cmd;
2276 var MgcpResponse resp;
2277 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2278 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2279 var MgcpCallId call_id_1 := '8376F297'H;
2280 var MgcpCallId call_id_2 := '837AF2A7'H;
2281
2282 f_init();
2283
2284 /* ep_1 and ep_2 are overlapping, selecting both one after
2285 * another should work fine: */
2286 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2287 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2288 f_dlcx_ok(ep_1);
2289 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2290 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2291 f_dlcx_ok(ep_2);
2292
2293 /* When ep_1 is serving a call we can not select ep_2 becaus
2294 * it is overlapping with ep_1 */
2295 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2296 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2297 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2298 resp := mgcp_transceive_mgw(cmd, ?);
2299 if (resp.line.code != "501") {
2300 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2301 }
2302 f_dlcx_ok(ep_1);
2303
2304 setverdict(pass);
2305 }
2306
2307 /* Create one connection in loopback mode, test if the RTP packets are
2308 * actually reflected */
2309 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2310 var RtpFlowData flow;
2311 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2312 var MgcpCallId call_id := '12250989'H;
2313 var RtpemStats stats;
2314
2315 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002316 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002317 flow.em.portnr := 10000;
2318 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2319
2320 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2321 f_sleep(1.0);
2322 f_flow_delete(RTPEM[0], ep, call_id);
2323
2324 stats := f_rtpem_stats_get(RTPEM[0]);
2325
2326 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2327 setverdict(fail);
2328 }
2329 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2330 setverdict(fail);
2331 }
2332
2333 f_rtpem_stats_err_check(stats);
2334
2335 setverdict(pass);
2336 }
2337
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002338 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2339 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2340 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2341 var template MgcpCommand cmd;
2342 var MgcpResponse resp;
2343 var MgcpCallId call_id := '1234'H;
2344 var MgcpConnectionId conn_id;
2345
2346 f_init(ep);
2347
2348 /* create the connection on the MGW */
2349 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2350 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2351 conn_id := extract_conn_id(resp);
2352
2353 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2354 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2355 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2356 valueof(ts_SDP_ptime(20)) });
2357 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2358
2359 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2360 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2361 }
2362 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2363 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2364 }
2365
2366 /* clean-up */
2367 f_dlcx_ok(ep, call_id);
2368 setverdict(pass);
2369 }
2370
2371 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2372 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2373 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2374 var template MgcpCommand cmd;
2375 var MgcpResponse resp;
2376 var MgcpCallId call_id := '1234'H;
2377 var MgcpConnectionId conn_id;
2378
2379 f_init(ep);
2380
2381 /* create the connection on the MGW */
2382 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2383 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2384 conn_id := extract_conn_id(resp);
2385
2386 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2387 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2388 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2389 valueof(ts_SDP_ptime(20)) });
2390 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2391
2392 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2393 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2394 }
2395 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2396 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2397 }
2398
2399 /* clean-up */
2400 f_dlcx_ok(ep, call_id);
2401 setverdict(pass);
2402 }
2403
Harald Welte00a067f2017-09-13 23:27:17 +02002404 control {
2405 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002406 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002407 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002408 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002409 execute(TC_crcx_unsupp_mode());
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +02002410 execute(TC_crcx_osmo_ign());
Harald Weltee636afd2017-09-17 16:24:09 +08002411 execute(TC_crcx_early_bidir_mode());
2412 execute(TC_crcx_unsupp_param());
2413 execute(TC_crcx_missing_callid());
2414 execute(TC_crcx_missing_mode());
2415 execute(TC_crcx_unsupp_packet_intv());
2416 execute(TC_crcx_illegal_double_lco());
2417 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002418 execute(TC_crcx_wildcarded());
2419 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002420 execute(TC_mdcx_without_crcx());
2421 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002422 execute(TC_mdcx_wildcarded());
2423 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002424 execute(TC_crcx_and_dlcx_ep_callid_connid());
2425 execute(TC_crcx_and_dlcx_ep_callid());
2426 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002427 execute(TC_crcx_and_dlcx_ep_callid_inval());
2428 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002429 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002430
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002431 execute(TC_crcx_osmux_wildcard());
2432 execute(TC_crcx_osmux_fixed());
2433 execute(TC_crcx_osmux_fixed_twice());
2434 execute(TC_one_crcx_receive_only_osmux());
2435 execute(TC_one_crcx_loopback_osmux());
2436 execute(TC_two_crcx_and_rtp_osmux());
2437 execute(TC_two_crcx_and_rtp_osmux_bidir());
2438 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2439 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2440
Harald Welte33d82162017-12-28 03:21:57 +01002441 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002442
2443 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002444
2445 execute(TC_one_crcx_receive_only_rtp());
2446 execute(TC_one_crcx_loopback_rtp());
Philipp Maierc0ca42d2021-05-17 18:41:19 +02002447 execute(TC_one_crcx_loopback_rtp_ipv6());
Philipp Maier1ac13982021-05-07 23:06:07 +02002448 execute(TC_one_crcx_loopback_rtp_implicit());
Harald Weltebb7523b2018-03-29 08:52:01 +02002449 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002450 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002451 execute(TC_two_crcx_diff_pt_and_rtp());
2452 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002453 execute(TC_two_crcx_mdcx_and_rtp());
2454 execute(TC_two_crcx_and_unsolicited_rtp());
2455 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002456 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002457 execute(TC_amr_oa_bwe_rtp_conversion());
2458 execute(TC_amr_oa_oa_rtp_conversion());
2459 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002460
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002461 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002462
2463 execute(TC_e1_crcx_and_dlcx_ep());
2464 execute(TC_e1_crcx_with_overlap());
2465 execute(TC_e1_crcx_loopback());
2466
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002467 execute(TC_crcx_mdcx_ip4());
2468 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002469 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2470 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2471 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2472 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2473 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2474 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2475 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2476 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Harald Welte00a067f2017-09-13 23:27:17 +02002477 }
2478}