blob: 1a45015a371a912fb0db3986ebc47b1538b4c687 [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;
67 charstring mp_local_ip := "127.0.0.1";
68 PortNumber mp_remote_udp_port := 2427;
69 charstring mp_remote_ip := "127.0.0.1";
Harald Weltef07c2862017-11-18 17:16:24 +010070 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020071 PortNumber mp_local_osmux_port := 1985;
Oliver Smith280536b2019-06-28 11:04:48 +020072
73 /* Whether to enable conn-timeout tests. Can be dropped completely and enabled unconditionally once new
74 * version of osmo-mgw is released (current version: 1.5.0) */
75 boolean mp_enable_conn_timeout_test := true;
Harald Welte3c6ebb92017-09-16 00:56:57 +080076 }
77
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020078 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
79 /* Turn on conversion mode */
80 f_vty_enter_config(MGWVTY);
81 f_vty_transceive(MGWVTY, "mgcp");
82 if (osmux_on) {
83 f_vty_transceive(MGWVTY, "osmux on");
84 } else {
85 f_vty_transceive(MGWVTY, "osmux off");
86 }
87 f_vty_transceive(MGWVTY, "exit");
88 f_vty_transceive(MGWVTY, "exit");
89
90 }
91
92 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +010093 map(self:MGWVTY, system:MGWVTY);
94 f_vty_set_prompts(MGWVTY);
95 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020096
97 f_vty_enable_osmux(osmux_on);
Oliver Smith280536b2019-06-28 11:04:48 +020098 if (mp_enable_conn_timeout_test) {
99 f_vty_config(MGWVTY, "mgcp", "conn-timeout 0");
100 }
Philipp Maier6137c322019-02-20 16:13:41 +0100101 }
102
Harald Weltebb7523b2018-03-29 08:52:01 +0200103 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
104 runs on dummy_CT {
105 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
106 map(comp_ref:RTP, system:RTP);
107 map(comp_ref:RTCP, system:RTCP);
108 comp_ref.start(RTP_Emulation.f_main());
109 }
110
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200111 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
112 runs on dummy_CT {
113 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
114 map(comp_ref:OSMUX, system:OSMUX);
115 comp_ref.start(OSMUX_Emulation.f_main());
116 }
117
Harald Welte21ba5572017-09-19 17:55:05 +0800118 /* initialization function, called by each test case at the
119 * beginning, but 'initialized' variable ensures its body is
120 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200121 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800122 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100123 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200124
Harald Welteedc45c12017-11-18 19:15:05 +0100125 if (initialized == false) {
126 initialized := true;
127
128 /* some random number for the initial transaction id */
129 g_trans_id := float2int(rnd()*65535.0);
130 map(self:MGCP, system:MGCP_CODEC_PT);
131 /* connect the MGCP test port using the given
132 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
133 * */
134 res := MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, mp_remote_ip, mp_remote_udp_port, mp_local_ip, mp_local_udp_port, 0, { udp := {} });
Harald Welte9220f632018-05-23 20:27:02 +0200135 if (not ispresent(res.connId)) {
136 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200137 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200138 }
Harald Welteedc45c12017-11-18 19:15:05 +0100139 g_mgcp_conn_id := res.connId;
140
Harald Weltebb7523b2018-03-29 08:52:01 +0200141 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
142 f_rtpem_init(vc_RTPEM[i], i);
143 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
144 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200145 if (osmux_on) {
146 f_osmuxem_init(vc_OsmuxEM);
147 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
148 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800149 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800150
Harald Welteedc45c12017-11-18 19:15:05 +0100151 if (isvalue(ep)) {
152 /* do a DLCX on all connections of the EP */
153 f_dlcx_ignore(valueof(ep));
154 }
Philipp Maier6137c322019-02-20 16:13:41 +0100155
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200156 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800157 }
158
Harald Welte00a067f2017-09-13 23:27:17 +0200159 testcase TC_selftest() runs on dummy_CT {
160 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 +0100161 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 +0200162 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
163 "I: 1\n" &
164 "\n" &
165 "v=0\r\n" &
166 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
167 "s=-\r\n" &
168 "c=IN IP4 0.0.0.0\r\n" &
169 "t=0 0\r\n" &
170 "m=audio 0 RTP/AVP 126\r\n" &
171 "a=rtpmap:126 AMR/8000\r\n" &
172 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100173 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 +0200174 "M: sendrecv\r" &
175 "C: 2\r\n" &
176 "I: 1\r\n" &
177 "L: p:20, a:AMR, nt:IN\r\n" &
178 "\n" &
179 "v=0\r\n" &
180 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800181 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200182 "c=IN IP4 0.0.0.0\r\n" &
183 "t=0 0\r\n" &
184 "m=audio 4441 RTP/AVP 99\r\n" &
185 "a=rtpmap:99 AMR/8000\r\n" &
186 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800187 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200188
189 log(c_auep);
190 log(dec_MgcpCommand(c_auep));
191
192 log(c_mdcx3);
193 log(dec_MgcpCommand(c_mdcx3));
194
195 log(c_mdcx3_ret);
196 log(dec_MgcpResponse(c_mdcx3_ret));
197
198 log(c_mdcx4);
199 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800200
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100201 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
202 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 +0800203
204 log(c_crcx510_ret);
205 log(dec_MgcpResponse(c_crcx510_ret));
206 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100207
208 /* We didn't encounter any DTE, so pass the test */
209 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800210 }
211
Harald Weltee636afd2017-09-17 16:24:09 +0800212 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100213 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800214 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
215 * - CRCX with remote session description and without
216 *
217 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100218 * x packetization != 20ms
219 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800220 * x unsupported mode (517)
221 * x bidirectional mode before RemoteConnDesc: 527
222 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100223 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800224 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
225 */
226
Harald Welte21ba5572017-09-19 17:55:05 +0800227 /* build a receive template for receiving a MGCP message. You
228 * pass the MGCP response template in, and it will generate an
229 * MGCP_RecvFrom template that can match the primitives arriving on the
230 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800231 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
232 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100233 connId := g_mgcp_conn_id,
Harald Weltee636afd2017-09-17 16:24:09 +0800234 remName := mp_remote_ip,
235 remPort := mp_remote_udp_port,
236 locName := mp_local_ip,
237 locPort := mp_local_udp_port,
238 msg := { response := resp }
239 }
240 return mrf;
241 }
242
243 /* Send a MGCP request + receive a (matching!) response */
244 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
245 var MgcpMessage msg := { command := valueof(cmd) };
246 resp.line.trans_id := cmd.line.trans_id;
247 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800248 var MGCP_RecvFrom mrf;
249 timer T := 5.0;
250
Harald Welte55015362017-11-18 16:02:42 +0100251 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800252 T.start;
253 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800254 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200255 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
256 setverdict(fail, "Response didn't match template");
257 mtc.stop;
258 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800259 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200260 [] T.timeout {
261 setverdict(fail, "Timeout waiting for response to ", cmd);
262 mtc.stop;
263 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800264 }
265 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800266
267 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
268 return mrf.msg.response;
269 } else {
270 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
271 return r;
272 }
Harald Welte00a067f2017-09-13 23:27:17 +0200273 }
274
Harald Welteba62c8c2017-11-18 18:26:49 +0100275 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
276 var integer i;
277 for (i := 0; i < lengthof(resp.params); i := i + 1) {
278 var MgcpParameter par := resp.params[i];
279 if (par.code == "I") {
280 return str2hex(par.val);
281 }
282 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200283 setverdict(fail, "Could not find conn id for MgcpReponse");
284 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100285 return '00000000'H;
286 }
287
Harald Welte10889c12017-11-18 19:40:31 +0100288 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
289 template MgcpCallId call_id := omit,
290 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100291 var template MgcpCommand cmd;
292 var MgcpResponse resp;
293 var template MgcpResponse rtmpl := {
294 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100295 code := ret_code,
296 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100297 },
298 params := *,
299 sdp := *
300 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100301 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100302 resp := mgcp_transceive_mgw(cmd, rtmpl);
303 }
304
Harald Welte10889c12017-11-18 19:40:31 +0100305 /* Send DLCX and expect OK response */
306 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
307 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100308 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100309 }
310
Harald Welteba62c8c2017-11-18 18:26:49 +0100311 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100312 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100313 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100314 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100315 }
316
Harald Weltebb7523b2018-03-29 08:52:01 +0200317 type record HostPort {
318 charstring hostname,
319 integer portnr optional
320 }
321 type record RtpFlowData {
322 HostPort em, /* emulation side */
323 HostPort mgw, /* mgw side */
324 uint7_t pt,
325 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200326 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100327 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200328 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
329 MgcpOsmuxCID osmux_cid optional,
330 MgcpOsmuxCID osmux_cid_response optional,
331 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100332 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200333 }
334
Philipp Maier2321ef92018-06-27 17:52:04 +0200335 /* Create an RTP flow (bidirectional, or receive-only) */
336 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 +0200337 boolean one_phase := true)
338 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200339 var template MgcpCommand cmd;
340 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100341 var SDP_attribute_list attributes;
342
343 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
344 if (isvalue(flow.fmtp)) {
345 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
346 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200347
348 /* bind local RTP emulation socket */
349 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
350
Philipp Maier28bb8292018-07-20 17:09:17 +0200351 /* configure rtp-emulation */
352 if (ispresent(flow.rtp_cfg)) {
353 f_rtpem_configure(pt, flow.rtp_cfg);
354 } else {
355 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
356 rtp_cfg.tx_payload_type := flow.pt
357 f_rtpem_configure(pt, rtp_cfg);
358 }
359
Harald Weltebb7523b2018-03-29 08:52:01 +0200360 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200361 /* Connect flow to MGW using a CRCX that also contains an SDP
362 * part that tells the MGW where we are listening for RTP streams
363 * that come from the MGW. We get a fully working connection in
364 * one go. */
365
366 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200367 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100368 flow.em.portnr, { int2str(flow.pt) }, attributes);
369
Harald Weltebb7523b2018-03-29 08:52:01 +0200370 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
371 flow.mgcp_conn_id := extract_conn_id(resp);
372 /* extract port number from response */
373 flow.mgw.portnr :=
374 resp.sdp.media_list[0].media_field.ports.port_number;
375 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200376 /* Create a half-open connection only. We do not tell the MGW
377 * where it can send RTP streams to us. This means this
378 * connection will only be able to receive but can not send
379 * data back to us. In order to turn the connection in a fully
380 * bi-directional one, a separate MDCX is needed. */
381
382 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200383 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
384 flow.mgcp_conn_id := extract_conn_id(resp);
385 /* extract MGW-side port number from response */
386 flow.mgw.portnr :=
387 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200388 }
389 /* finally, connect the emulation-side RTP socket to the MGW */
390 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
391 }
392
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200393 /* Create an Osmux flow (bidirectional, or receive-only) */
394 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
395 boolean one_phase := true)
396 runs on dummy_CT {
397 var template MgcpCommand cmd;
398 var MgcpResponse resp;
399 var SDP_attribute_list attributes;
400 var OsmuxTxHandle tx_hdl;
401 var OsmuxRxHandle rx_hdl;
402 var charstring cid_response;
403 var OsmuxCID cid_resp_parsed
404
405 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
406 if (isvalue(flow.fmtp)) {
407 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
408 }
409
410 /* bind local Osmux emulation socket */
411 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
412
413 /* configure osmux-emulation */
414 if (ispresent(flow.osmux_cfg)) {
415 f_osmuxem_configure(pt, flow.osmux_cfg);
416 } else {
417 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
418 f_osmuxem_configure(pt, osmux_cfg);
419 flow.osmux_cfg := osmux_cfg
420 }
421
422 if (one_phase) {
423 /* Connect flow to MGW using a CRCX that also contains an SDP
424 * part that tells the MGW where we are listening for Osmux streams
425 * that come from the MGW. We get a fully working connection in
426 * one go. */
427 rx_hdl := c_OsmuxemDefaultRxHandle;
428 rx_hdl.cid := flow.osmux_cid;
429 f_osmuxem_register_rxhandle(pt, rx_hdl);
430 flow.osmux_cid_sent := true;
431 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
432 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
433 flow.em.portnr, { int2str(flow.pt) }, attributes);
434 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
435 flow.mgcp_conn_id := extract_conn_id(resp);
436 /* extract port number from response */
437 flow.mgw.portnr :=
438 resp.sdp.media_list[0].media_field.ports.port_number;
439 } else {
440 /* Create a half-open connection only. We do not tell the MGW
441 * where it can send Osmux streams to us. This means this
442 * connection will only be able to receive but can not send
443 * data back to us. In order to turn the connection in a fully
444 * bi-directional one, a separate MDCX is needed. */
445
446 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
447 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
448
449 flow.mgcp_conn_id := extract_conn_id(resp);
450 /* extract MGW-side port number from response */
451 flow.mgw.portnr :=
452 resp.sdp.media_list[0].media_field.ports.port_number;
453 }
454
455 /* extract Osmux CID we got assigned by the MGW */
456 var MgcpMessage resp_msg := {
457 response := resp
458 }
459
460 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
461 setverdict(fail, "No Osmux CID in MGCP response", resp);
462 mtc.stop;
463 }
464
465 /* Make sure response is no wildcard */
466 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
467 if (flow.osmux_cid_response == -1) {
468 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
469 mtc.stop;
470 }
471 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
472 f_osmuxem_register_txhandle(pt, tx_hdl);
473
474 /* finally, connect the emulation-side RTP socket to the MGW */
475 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
476 }
477
Philipp Maier2321ef92018-06-27 17:52:04 +0200478 /* Modify an existing RTP flow */
479 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
480 runs on dummy_CT {
481 var template MgcpCommand cmd;
482 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100483 var SDP_attribute_list attributes;
484
485 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
486 if (isvalue(flow.fmtp)) {
487 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
488 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200489
490 /* rebind local RTP emulation socket to the new address */
491 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
492
Philipp Maier28bb8292018-07-20 17:09:17 +0200493 /* reconfigure rtp-emulation */
494 if (ispresent(flow.rtp_cfg)) {
495 f_rtpem_configure(pt, flow.rtp_cfg);
496 } else {
497 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
498 rtp_cfg.tx_payload_type := flow.pt
499 f_rtpem_configure(pt, rtp_cfg);
500 }
501
Philipp Maier2321ef92018-06-27 17:52:04 +0200502 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
503 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
504 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100505 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200506 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
507
508 /* extract MGW-side port number from response. (usually this
509 * will not change, but thats is up to the MGW) */
510 flow.mgw.portnr :=
511 resp.sdp.media_list[0].media_field.ports.port_number;
512
513 /* reconnect the emulation-side RTP socket to the MGW */
514 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
515 }
516
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200517 /* Modify an existing Osmux flow */
518 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
519 runs on dummy_CT {
520 var template MgcpCommand cmd;
521 var MgcpResponse resp;
522 var SDP_attribute_list attributes;
523 var OsmuxRxHandle rx_hdl;
524 var charstring cid_response;
525 var OsmuxCID cid_resp_parsed
526
527 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
528 if (isvalue(flow.fmtp)) {
529 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
530 }
531
532 /* rebind local Osmux emulation socket to the new address */
533 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
534
535 /* configure osmux-emulation */
536 if (ispresent(flow.osmux_cfg)) {
537 f_osmuxem_configure(pt, flow.osmux_cfg);
538 } else {
539 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
540 f_osmuxem_configure(pt, osmux_cfg);
541 }
542
543 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
544 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
545 rx_hdl := c_OsmuxemDefaultRxHandle;
546 rx_hdl.cid := flow.osmux_cid;
547 f_osmuxem_register_rxhandle(pt, rx_hdl);
548 flow.osmux_cid_sent := true;
549 }
550
551 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
552 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
553 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
554 flow.em.portnr, { int2str(flow.pt) }, attributes);
555 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
556
557 /* extract MGW-side port number from response. (usually this
558 * will not change, but thats is up to the MGW) */
559 flow.mgw.portnr :=
560 resp.sdp.media_list[0].media_field.ports.port_number;
561
562 /* extract Osmux CID we got assigned by the MGW */
563 var MgcpMessage resp_msg := {
564 response := resp
565 }
566
567 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
568 setverdict(fail, "No Osmux CID in MGCP response", resp);
569 mtc.stop;
570 }
571
572 /* Make sure response is no wildcard */
573 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
574 if (cid_resp_parsed == -1) {
575 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
576 mtc.stop;
577 }
578 if (cid_resp_parsed != flow.osmux_cid_response) {
579 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
580 mtc.stop;
581 }
582
583 /* reconnect the emulation-side Osmux socket to the MGW */
584 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
585 }
586
Philipp Maier2321ef92018-06-27 17:52:04 +0200587 /* Delete an existing RTP flow */
588 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
589 runs on dummy_CT {
590 var template MgcpCommand cmd;
591 var MgcpResponse resp;
592
593 /* Switch off RTP flow */
594 f_rtpem_mode(pt, RTPEM_MODE_NONE);
595
596 /* Delete connection on MGW (if needed) */
597 if (isvalue(call_id) and isvalue(ep)) {
598 f_sleep(0.1);
599 f_dlcx_ok(valueof(ep), call_id);
600 }
601 }
602
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200603 /* Delete an existing Osmux flow */
604 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
605 runs on dummy_CT {
606 var template MgcpCommand cmd;
607 var MgcpResponse resp;
608
609 /* Switch off Osmux flow */
610 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
611
612 /* Delete connection on MGW (if needed) */
613 if (isvalue(call_id) and isvalue(ep)) {
614 f_sleep(0.1);
615 f_dlcx_ok(valueof(ep), call_id);
616 }
617 }
618
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100619 function f_crcx(charstring ep_prefix) runs on dummy_CT {
620 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800621 var template MgcpCommand cmd;
622 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100623 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800624
Harald Welteedc45c12017-11-18 19:15:05 +0100625 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800626
Harald Welteba62c8c2017-11-18 18:26:49 +0100627 /* create the connection on the MGW */
628 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100629 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100630 extract_conn_id(resp);
631
632 /* clean-up */
633 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100634 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100635
Philipp Maier45635f42018-06-05 17:28:02 +0200636 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
637 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
638 var template MgcpCommand cmd;
639 var MgcpResponse resp;
640 var MgcpCallId call_id := '1234'H;
641
642 f_init(ep);
643
644 /* create the connection on the MGW */
645 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
646 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
647 extract_conn_id(resp);
648
649 /* clean-up */
650 f_dlcx_ok(ep, call_id);
651
652 /* See also OS#2658: Even when we omit the LCO information, we
653 expect the MGW to pick a sane payload type for us. This
654 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200655 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200656 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200657 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200658 }
659
660 /* See also OS#2658: We also expect the MGW to assign a port
661 number to us. */
662 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
663 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200664 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200665 }
666 }
667
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200668 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
669 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
670 var template MgcpCommand cmd;
671 var MgcpResponse resp;
672 var MgcpCallId call_id := '1234'H;
673 var charstring cid_response;
674
675 if (run_init) {
676 f_init(ep, true);
677 }
678
679 /* create the connection on the MGW */
680 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
681 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
682 extract_conn_id(resp);
683
684 /* extract Osmux CID we got assigned by the MGW */
685 var MgcpMessage resp_msg := {
686 response := resp
687 }
688
689 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
690 setverdict(fail, "No Osmux CID in MGCP response", resp);
691 mtc.stop;
692 }
693
694 /* Make sure response is no wildcard */
695 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
696 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
697 mtc.stop;
698 }
699
700 /* clean-up */
701 f_dlcx_ok(ep, call_id);
702 }
703
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100704 /* test valid CRCX without SDP */
705 testcase TC_crcx() runs on dummy_CT {
706 f_crcx(c_mgw_ep_rtpbridge);
707 setverdict(pass);
708 }
709
Philipp Maier45635f42018-06-05 17:28:02 +0200710 /* test valid CRCX without SDP and LCO */
711 testcase TC_crcx_no_lco() runs on dummy_CT {
712 f_crcx_no_lco(c_mgw_ep_rtpbridge);
713 setverdict(pass);
714 }
715
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100716 /* test valid CRCX without SDP (older method without endpoint prefix) */
717 testcase TC_crcx_noprefix() runs on dummy_CT {
718 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800719 setverdict(pass);
720 }
721
722 /* test CRCX with unsupported mode, expect 517 */
723 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
724 var template MgcpCommand cmd;
725 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100726 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100727 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800728 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
729
Harald Welteedc45c12017-11-18 19:15:05 +0100730 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800731
Harald Welteba62c8c2017-11-18 18:26:49 +0100732 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800733 resp := mgcp_transceive_mgw(cmd, rtmpl);
734 setverdict(pass);
735 }
736
Harald Welte21ba5572017-09-19 17:55:05 +0800737 /* test CRCX with early bi-directional mode, expect 527 as
738 * bi-diretional media can only be established once both local and
739 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800740 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
741 var template MgcpCommand cmd;
742 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100743 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100744 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800745 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
746
Harald Welteedc45c12017-11-18 19:15:05 +0100747 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800748
Harald Welteba62c8c2017-11-18 18:26:49 +0100749 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800750 resp := mgcp_transceive_mgw(cmd, rtmpl);
751 setverdict(pass);
752 }
753
754 /* test CRCX with unsupported Parameters */
755 testcase TC_crcx_unsupp_param() runs on dummy_CT {
756 var template MgcpCommand cmd;
757 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100758 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100759 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800760 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
761
Harald Welteedc45c12017-11-18 19:15:05 +0100762 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800763
Harald Welteba62c8c2017-11-18 18:26:49 +0100764 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100765 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
766 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
767
Harald Weltee636afd2017-09-17 16:24:09 +0800768 resp := mgcp_transceive_mgw(cmd, rtmpl);
769 setverdict(pass);
770 }
771
772 /* test CRCX with missing CallId */
773 testcase TC_crcx_missing_callid() runs on dummy_CT {
774 var template MgcpCommand cmd;
775 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100776 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100777 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800778
Harald Welteedc45c12017-11-18 19:15:05 +0100779 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800780
Harald Welteba62c8c2017-11-18 18:26:49 +0100781 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800782 cmd.params := {
783 t_MgcpParConnMode("recvonly"),
784 t_MgcpParLocConnOpt("p:20")
785 }
786 resp := mgcp_transceive_mgw(cmd, rtmpl);
787 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100788
Harald Weltee636afd2017-09-17 16:24:09 +0800789 }
790
791 /* test CRCX with missing Mode */
792 testcase TC_crcx_missing_mode() runs on dummy_CT {
793 var template MgcpCommand cmd;
794 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100795 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100796 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100797 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800798
Harald Welteedc45c12017-11-18 19:15:05 +0100799 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800800
Harald Welteba62c8c2017-11-18 18:26:49 +0100801 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800802 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100803 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800804 t_MgcpParLocConnOpt("p:20")
805 }
806 resp := mgcp_transceive_mgw(cmd, rtmpl);
807 setverdict(pass);
808 }
809
810 /* test CRCX with unsupported packetization interval */
811 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
812 var template MgcpCommand cmd;
813 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100814 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100815 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100816 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800817
Harald Welteedc45c12017-11-18 19:15:05 +0100818 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800819
Harald Welteba62c8c2017-11-18 18:26:49 +0100820 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100821 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800822 resp := mgcp_transceive_mgw(cmd, rtmpl);
823 setverdict(pass);
824 }
825
826 /* test CRCX with illegal double presence of local connection option */
827 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
828 var template MgcpCommand cmd;
829 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100830 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100831 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800832 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
833
Harald Welteedc45c12017-11-18 19:15:05 +0100834 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800835
Harald Welteba62c8c2017-11-18 18:26:49 +0100836 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100837 /* p:20 is permitted only once and not twice! */
838 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800839 resp := mgcp_transceive_mgw(cmd, rtmpl);
840 setverdict(pass);
841 }
842
843 /* test valid CRCX with valid SDP */
844 testcase TC_crcx_sdp() runs on dummy_CT {
845 var template MgcpCommand cmd;
846 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100847 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100848 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800849
Harald Welteedc45c12017-11-18 19:15:05 +0100850 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800851
Harald Welteba62c8c2017-11-18 18:26:49 +0100852 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800853 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
854 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
855 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100856 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100857
858 /* clean-up */
859 f_dlcx_ok(ep, call_id);
860
Harald Weltee636afd2017-09-17 16:24:09 +0800861 setverdict(pass);
862 }
863
Philipp Maier5e06cee2018-02-01 18:28:08 +0100864 /* test valid wildcarded CRCX */
865 testcase TC_crcx_wildcarded() runs on dummy_CT {
866 var template MgcpCommand cmd;
867 var MgcpResponse resp;
868 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
869 var MgcpCallId call_id := '1234'H;
870 var MgcpEndpoint ep_assigned;
871 f_init();
872
873 /* create the connection on the MGW */
874 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
875 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
876 extract_conn_id(resp);
877
878 /* extract endpoint name we got assigned by the MGW */
879 var MgcpMessage resp_msg := {
880 response := resp
881 }
882 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
883 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200884 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100885 }
886
887 /* clean-up */
888 f_dlcx_ok(ep_assigned, call_id);
889
890 setverdict(pass);
891 }
892
893 /* test valid wildcarded CRCX */
894 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
895 const integer n_endpoints := 32;
896 var integer i;
897 var template MgcpCommand cmd;
898 var MgcpResponse resp;
899 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
900 var MgcpCallId call_id := '1234'H;
901 var MgcpEndpoint ep_assigned[n_endpoints];
902 f_init();
903
904 /* Exhaust all endpoint resources on the virtual trunk */
905 for (i := 0; i < n_endpoints; i := i+1) {
906 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
907 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
908
909 /* Make sure we got a connection id */
910 extract_conn_id(resp);
911
912 var MgcpMessage resp_msg := {
913 response := resp
914 }
915 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
916 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200917 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100918 }
919 }
920
921 /* Try to allocate one more endpoint, which should fail */
922 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
923 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
924 resp := mgcp_transceive_mgw(cmd, rtmpl);
925 setverdict(pass);
926
927 /* clean-up */
928 for (i := 0; i < n_endpoints; i := i+1) {
929 f_dlcx_ok(ep_assigned[i], call_id);
930 }
931 setverdict(pass);
932 }
933
Harald Weltee636afd2017-09-17 16:24:09 +0800934 /* TODO: various SDP related bits */
935
936
937 /* TODO: CRCX with X-Osmux */
938 /* TODO: double CRCX without force_realloc */
939
940 /* TODO: MDCX (various) */
941
942 /* TODO: MDCX without CRCX first */
943 testcase TC_mdcx_without_crcx() runs on dummy_CT {
944 var template MgcpCommand cmd;
945 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100946 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100947 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800948 var template MgcpResponse rtmpl := {
949 line := {
950 /* TODO: accept/enforce better error? */
951 code := "400",
952 string := ?
953 },
954 params:= { },
955 sdp := omit
956 };
957
Harald Welteedc45c12017-11-18 19:15:05 +0100958 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800959
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100960 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800961 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
962 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
963 valueof(ts_SDP_ptime(20)) });
964 resp := mgcp_transceive_mgw(cmd, rtmpl);
965 setverdict(pass);
966 }
967
968 /* DLCX without CRCX first */
969 testcase TC_dlcx_without_crcx() runs on dummy_CT {
970 var template MgcpCommand cmd;
971 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100972 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800973 var template MgcpResponse rtmpl := {
974 line := {
Harald Weltef91edf32017-12-28 14:16:21 +0100975 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +0800976 string := ?
977 },
978 params:= { },
979 sdp := omit
980 };
981
Harald Welteedc45c12017-11-18 19:15:05 +0100982 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800983
Harald Welteedc45c12017-11-18 19:15:05 +0100984 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800985 resp := mgcp_transceive_mgw(cmd, rtmpl);
986 setverdict(pass);
987 }
988
Philipp Maier8a3dc922018-02-02 14:55:12 +0100989 /* test valid wildcarded MDCX */
990 testcase TC_mdcx_wildcarded() runs on dummy_CT {
991 /* Note: A wildcarded MDCX is not allowed, so we expect the
992 * MGW to reject this request */
993 var template MgcpCommand cmd;
994 var MgcpResponse resp;
995 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
996 var MgcpCallId call_id := '1225'H;
997 var template MgcpResponse rtmpl := {
998 line := {
999 /* TODO: accept/enforce better error? */
1000 code := "507",
1001 string := ?
1002 },
1003 params:= { },
1004 sdp := omit
1005 };
1006
1007 f_init(ep);
1008
1009 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1010 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1011 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1012 valueof(ts_SDP_ptime(20)) });
1013 resp := mgcp_transceive_mgw(cmd, rtmpl);
1014 setverdict(pass);
1015 }
1016
1017 /* test valid wildcarded DLCX */
1018 testcase TC_dlcx_wildcarded() runs on dummy_CT {
1019 /* Note: A wildcarded DLCX is specified, but our MGW does not
1020 * support this feature so we expect the MGW to reject the
1021 * request */
1022 var template MgcpCommand cmd;
1023 var MgcpResponse resp;
1024 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1025 var template MgcpResponse rtmpl := {
1026 line := {
1027 code := "507",
1028 string := ?
1029 },
1030 params:= { },
1031 sdp := omit
1032 };
1033
1034 f_init(ep);
1035
1036 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1037 resp := mgcp_transceive_mgw(cmd, rtmpl);
1038 setverdict(pass);
1039 }
1040
Harald Welte79181ff2017-11-18 19:26:11 +01001041 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1042 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1043 var template MgcpCommand cmd;
1044 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001045 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001046 var MgcpCallId call_id := '51234'H;
1047
1048 f_init(ep);
1049
1050 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1051 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1052
1053 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1054
1055 setverdict(pass);
1056 }
1057
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001058 /* test valid CRCX without SDP */
1059 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1060 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1061 setverdict(pass);
1062 }
1063
1064 /* test valid CRCX without SDP */
1065 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1066 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1067 setverdict(pass);
1068 }
1069
1070 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1071 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1072 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1073 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1074 setverdict(pass);
1075 }
1076
1077 /* Create one half open connection in receive-only mode. The MGW must accept
1078 * the packets but must not send any. */
1079 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1080 var RtpFlowData flow;
1081 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1082 var MgcpCallId call_id := '1225'H;
1083 var OsmuxemStats stats;
1084 var OsmuxTxHandle tx_hdl;
1085
1086 f_init(ep, true);
1087 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
1088 flow.em.portnr := mp_local_osmux_port;
1089 flow.osmux_cid := -1;
1090 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1091
1092 /* create a transmitter not yet known by MGW */
1093 tx_hdl := valueof(t_TxHandleAMR590(2));
1094 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1095
1096 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1097 f_sleep(1.0);
1098 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1099
1100 stats := f_osmuxem_stats_get(OsmuxEM);
1101
1102 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1103 setverdict(fail);
1104 }
1105 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1106 setverdict(fail);
1107 }
1108
1109 f_osmuxem_stats_err_check(stats);
1110
1111 setverdict(pass);
1112 }
1113
1114 /* Create one connection in loopback mode, test if the Osmux packets are
1115 * actually reflected */
1116 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1117 var RtpFlowData flow;
1118 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1119 var MgcpCallId call_id := '1225'H;
1120 var OsmuxemStats stats;
1121 var OsmuxTxHandle tx_hdl;
1122
1123 f_init(ep, true);
1124 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1125 flow.em.portnr := mp_local_osmux_port;
1126 flow.osmux_cid := 2;
1127 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1128
1129 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1130 f_sleep(1.0);
1131
1132 /* Switch off both Tx, wait to receive delayed frames from MGW */
1133 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1134 f_sleep(0.1);
1135 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1136
1137 stats := f_osmuxem_stats_get(OsmuxEM);
1138
1139 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1140 setverdict(fail);
1141 }
1142 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1143 setverdict(fail);
1144 }
1145
1146 f_osmuxem_stats_err_check(stats);
1147
1148 setverdict(pass);
1149 }
1150
1151 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1152 * must match the reception statistics on the other side and vice versa. The
1153 * user may also supply a tolerance value (number of packets) when deviations
1154 * are acceptable */
1155 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1156 var integer plen;
1157
1158 log("stats A: ", a);
1159 log("stats B: ", b);
1160 log("tolerance: ", tolerance, " packets");
1161 log("batch_size: ", batch_size, " packets");
1162
1163 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1164
1165 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1166 return false;
1167 }
1168
1169 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1170 return false;
1171 }
1172
1173 if(a.num_pkts_tx > 0) {
1174 plen := a.bytes_payload_tx / a.num_pkts_tx;
1175 } else {
1176 plen := 0;
1177 }
1178
1179 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1180 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1181 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1182 return false;
1183 }
1184
1185 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) {
1186 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1187 return false;
1188 }
1189
1190 return true;
1191 }
1192
1193 function f_TC_two_crcx_and_rtp_osmux(boolean bidir) runs on dummy_CT {
1194 var RtpFlowData flow[2];
1195 var RtpemStats stats_rtp;
1196 var OsmuxemStats stats_osmux;
1197 var MgcpResponse resp;
1198 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1199 var MgcpCallId call_id := '1226'H;
1200 var integer tolerance := 0;
1201
1202 f_init(ep, true);
1203
1204 /* from us to MGW */
1205 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1206 flow[0].rtp_cfg := c_RtpemDefaultCfg
1207 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1208 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1209 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);
1210 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1211 /* bind local RTP emulation sockets */
1212 flow[0].em.portnr := 10000;
1213 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1214
1215 /* from MGW back to us */
1216 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "AMR/8000"));
1217 flow[1].em.portnr := mp_local_osmux_port;
1218 flow[1].osmux_cid := 2;
1219 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1220 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1221
1222 if (bidir) {
1223 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1224 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1225
1226 /* Note: When we test bidirectional we may
1227 * loose packets during switch off because
1228 * both ends are transmitting and we only
1229 * can switch them off one by one. */
1230 tolerance := 3;
1231 } else {
1232 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1233 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1234 }
1235
1236 f_sleep(1.0);
1237
1238 /* Switch off both Tx, wait to receive delayed frames from MGW */
1239 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1240 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1241 f_sleep(0.1);
1242
1243 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1244 f_flow_delete(RTPEM[1]);
1245
1246 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1247 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1248 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1249 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1250 mtc.stop;
1251 }
1252
1253 f_rtpem_stats_err_check(stats_rtp);
1254 f_osmuxem_stats_err_check(stats_osmux);
1255
1256 setverdict(pass);
1257 }
1258
1259 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1260 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
1261 f_TC_two_crcx_and_rtp_osmux(false);
1262 }
1263
1264 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1265 * exchange some data in both directions */
1266 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
1267 f_TC_two_crcx_and_rtp_osmux(true);
1268 }
1269
1270
1271 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard) runs on dummy_CT {
1272 var RtpFlowData flow[2];
1273 var RtpemStats stats_rtp;
1274 var OsmuxemStats stats_osmux;
1275 var MgcpResponse resp;
1276 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1277 var MgcpCallId call_id := '1227'H;
1278 var integer num_pkts_tx[2];
1279 var integer temp;
1280
1281 f_init(ep, true);
1282
1283 /* Create the first connection in receive only mode */
1284 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1285 flow[0].rtp_cfg := c_RtpemDefaultCfg
1286 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1287 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1288 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);
1289 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1290 /* bind local RTP emulation sockets */
1291 flow[0].em.portnr := 10000;
1292 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1293
1294
1295 /* Create the second connection. This connection will be also
1296 * in receive only mode */
1297 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "AMR/8000"));
1298 flow[1].em.portnr := mp_local_osmux_port;
1299 if (crcx_osmux_wildcard) {
1300 flow[1].osmux_cid := -1;
1301 } else {
1302 flow[1].osmux_cid := 2;
1303 }
1304 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1305 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], false);
1306
1307
1308 /* The first leg starts transmitting */
1309 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1310 f_sleep(0.5);
1311 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1312 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1313 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1314 mtc.stop;
1315 }
1316 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1317 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1318 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1319 mtc.stop;
1320 }
1321
1322 /* The second leg starts transmitting a little later */
1323 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1324 f_sleep(1.0);
1325 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1326 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1327 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1328 mtc.stop;
1329 }
1330 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1331 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1332 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1333 mtc.stop;
1334 }
1335
1336 /* The first leg will now be switched into bidirectional
1337 * mode, but we do not expect any data comming back yet. */
1338 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1339 f_sleep(0.5);
1340 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1341 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1342 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1343 mtc.stop;
1344 }
1345 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1346 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1347 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1348 mtc.stop;
1349 }
1350
1351 /* When the second leg is switched into bidirectional mode
1352 * as well, then the MGW will connect the two together and
1353 * we should see RTP streams passing through from both ends. */
1354 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1355 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1356 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1357 num_pkts_tx[0] := stats_rtp.num_pkts_tx
1358 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1359 num_pkts_tx[1] := stats_osmux.num_pkts_tx
1360
1361 if (crcx_osmux_wildcard) {
1362 /* For now we must set same CID as the MGW recvCID,
1363 * having sendCID!=recvCID is not yet supported. */
1364 flow[1].osmux_cid := flow[1].osmux_cid_response;
1365 }
1366 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1367 f_sleep(2.0);
1368
1369 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1370 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1371
1372 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1373 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1374 log("stats_rtp: ", stats_rtp);
1375 log("stats_osmux: ", stats_osmux);
1376 log("old_rtp_tx: ", num_pkts_tx[0]);
1377 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1378 mtc.stop;
1379 }
1380
1381 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1382 if (temp > 3 or temp < -3) {
1383 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1384 mtc.stop;
1385 }
1386
1387 f_rtpem_stats_err_check(stats_rtp);
1388 f_osmuxem_stats_err_check(stats_osmux);
1389
1390 /* Tear down */
1391 f_flow_delete(RTPEM[0]);
1392 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1393 setverdict(pass);
1394 }
1395
1396 /* create one RTP and one OSmux emulations and pass data in both
1397 directions. Create CRCX with wildcard Osmux CID and set it later
1398 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1399 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
1400 f_two_crcx_mdcx_and_rtp_osmux(true);
1401 }
1402
1403 /* create one RTP and one OSmux emulations and pass data in both
1404 directions. Create CRCX with fixed Osmux CID and keep it during
1405 MDCX. This is similar to how BSC sets up the call in AoIP. */
1406 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
1407 f_two_crcx_mdcx_and_rtp_osmux(false);
1408 }
1409
Harald Welte646ecdb2017-12-28 03:21:57 +01001410 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1411 var template MgcpCommand cmd;
1412 var MgcpResponse resp;
1413
1414 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1415 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1416
1417 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1418
1419 setverdict(pass);
1420 }
1421
1422 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1423 var MgcpEndpoint ep;
1424 var MgcpCallId call_id;
1425 var integer ep_nr;
1426
1427 f_init();
1428
1429 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001430 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
Harald Welte646ecdb2017-12-28 03:21:57 +01001431 call_id := int2hex(ep_nr, 2) & '1234'H;
1432 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1433 }
1434 }
1435
Harald Welte79181ff2017-11-18 19:26:11 +01001436 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1437 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001438 var template MgcpCommand cmd;
1439 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001440 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001441 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001442
Harald Welteedc45c12017-11-18 19:15:05 +01001443 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001444
Harald Welteba62c8c2017-11-18 18:26:49 +01001445 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001446 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001447
Harald Welteba62c8c2017-11-18 18:26:49 +01001448 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001449
1450 setverdict(pass);
1451 }
1452
Harald Welte79181ff2017-11-18 19:26:11 +01001453 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1454 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1455 var template MgcpCommand cmd;
1456 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001457 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001458 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001459
1460 f_init(ep);
1461
1462 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1463 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1464
1465 f_dlcx_ok(ep);
1466
1467 setverdict(pass);
1468 }
1469
1470
Harald Welte6d167f82017-11-18 19:41:35 +01001471 /* CRCX + DLCX of valid endpoint but invalid call-id */
1472 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1473 var template MgcpCommand cmd;
1474 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001475 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001476 var MgcpCallId call_id := '51231'H;
1477
1478 f_init(ep);
1479
1480 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1481 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1482
1483 f_dlcx(ep, "516", *, 'ffff'H);
1484
1485 setverdict(pass);
1486 }
1487
1488
1489 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1490 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1491 var template MgcpCommand cmd;
1492 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001493 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001494 var MgcpCallId call_id := '51230'H;
1495
1496 f_init(ep);
1497
1498 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1499 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1500
1501 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1502
1503 setverdict(pass);
1504 }
1505
1506
Harald Weltee636afd2017-09-17 16:24:09 +08001507 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001508 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1509 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 Weltef53f1642017-11-18 19:57:11 +01001512 var MgcpCallId call_id := '51229'H;
1513 var template MgcpResponse rtmpl := {
1514 line := {
1515 code := "200",
1516 string := "OK"
1517 },
1518 params:= { },
1519 sdp := omit
1520 };
1521
1522 f_init(ep);
1523
1524 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1525 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1526
1527 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1528 resp := mgcp_transceive_mgw(cmd, rtmpl);
1529 resp := mgcp_transceive_mgw(cmd, rtmpl);
1530
1531 setverdict(pass);
1532 }
1533
Harald Weltebb7523b2018-03-29 08:52:01 +02001534 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1535 charstring codec) := {
1536 em := {
1537 hostname := host_a,
1538 portnr := omit
1539 },
1540 mgw := {
1541 hostname := host_b,
1542 portnr := omit
1543 },
1544 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001545 codec := codec,
1546 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001547 }
Harald Weltef53f1642017-11-18 19:57:11 +01001548
Harald Weltebb7523b2018-03-29 08:52:01 +02001549 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1550 testcase TC_rtpem_selftest() runs on dummy_CT {
1551 var RtpemStats stats[2];
1552 var integer local_port := 10000;
1553 var integer local_port2 := 20000;
1554
1555 f_init();
1556
1557 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1558 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1559
1560 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1561 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1562
1563 log("=== starting");
1564 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1565 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1566
1567 f_sleep(5.0);
1568
1569 log("=== stopping");
1570 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1571 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1572 f_sleep(0.5);
1573 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1574 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1575
1576 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1577 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1578 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1579 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001580 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001581 }
1582 setverdict(pass);
1583 }
1584
Philipp Maier2321ef92018-06-27 17:52:04 +02001585 /* Create one half open connection in receive-only mode. The MGW must accept
1586 * the packets but must not send any. */
1587 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1588 var RtpFlowData flow;
1589 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1590 var MgcpCallId call_id := '1225'H;
1591 var RtpemStats stats;
1592
1593 f_init(ep);
1594 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
1595 flow.em.portnr := 10000;
1596 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, false);
1597
1598 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1599 f_sleep(1.0);
1600 f_flow_delete(RTPEM[0], ep, call_id);
1601
1602 stats := f_rtpem_stats_get(RTPEM[0]);
1603
Philipp Maier42b17cc2019-10-01 13:53:17 +02001604 /* Make sure that at least some amount of RTP packets/bytes
1605 * have has been transmitted. The compare values for
1606 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1607 * using a testrun and the results were devided by 2, so even
1608 * in load situations we should reach the minimum amount of
1609 * required packets/bytes */
1610
1611 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001612 setverdict(fail);
1613 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001614 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001615 setverdict(fail);
1616 }
Philipp Maier36291392018-07-25 09:40:44 +02001617
1618 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001619
1620 setverdict(pass);
1621 }
1622
1623 /* Create one connection in loopback mode, test if the RTP packets are
1624 * actually reflected */
1625 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
1626 var RtpFlowData flow;
1627 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1628 var MgcpCallId call_id := '1225'H;
1629 var RtpemStats stats;
1630
1631 f_init(ep);
1632 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1633 flow.em.portnr := 10000;
1634 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
1635
1636 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1637 f_sleep(1.0);
1638 f_flow_delete(RTPEM[0], ep, call_id);
1639
1640 stats := f_rtpem_stats_get(RTPEM[0]);
1641
1642 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1643 setverdict(fail);
1644 }
1645 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1646 setverdict(fail);
1647 }
Philipp Maier36291392018-07-25 09:40:44 +02001648
1649 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001650
1651 setverdict(pass);
1652 }
1653
Philipp Maier7df85f62018-07-25 10:26:09 +02001654 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1655 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001656 var RtpFlowData flow[2];
1657 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001658 var MgcpResponse resp;
1659 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1660 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001661 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001662
1663 f_init(ep);
1664
1665 /* from us to MGW */
Philipp Maier7df85f62018-07-25 10:26:09 +02001666 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001667 /* bind local RTP emulation sockets */
1668 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001669 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001670
1671 /* from MGW back to us */
Philipp Maier7df85f62018-07-25 10:26:09 +02001672 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001673 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001674 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1675
1676 if (bidir) {
1677 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1678 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1679
1680 /* Note: When we test bidirectional we may
1681 * loose packets during switch off because
1682 * both ends are transmitting and we only
1683 * can switch them off one by one. */
1684 tolerance := 3;
1685 } else {
1686 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1687 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1688 }
1689
1690 f_sleep(1.0);
1691
1692 f_flow_delete(RTPEM[1]);
1693 f_flow_delete(RTPEM[0], ep, call_id);
1694
1695 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1696 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1697 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1698 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001699 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001700 }
1701
Philipp Maier36291392018-07-25 09:40:44 +02001702 f_rtpem_stats_err_check(stats[0]);
1703 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001704
Philipp Maier2321ef92018-06-27 17:52:04 +02001705 setverdict(pass);
1706 }
1707
1708 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1709 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001710 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001711 }
1712
1713 /* create two local RTP emulations; create two connections on MGW EP,
1714 * exchange some data in both directions */
1715 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001716 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1717 }
1718
1719 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1720 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1721 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1722 }
1723
1724 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1725 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1726 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001727 }
1728
1729 /* create two local RTP emulations and pass data in both directions */
1730 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1731 var RtpFlowData flow[2];
1732 var RtpemStats stats[2];
1733 var MgcpResponse resp;
1734 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1735 var MgcpCallId call_id := '1227'H;
1736 var integer num_pkts_tx[2];
1737 var integer temp;
1738
1739 f_init(ep);
1740
1741 /* Create the first connection in receive only mode */
1742 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1743 flow[0].em.portnr := 10000;
1744 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1745
1746 /* Create the second connection. This connection will be also
1747 * in receive only mode */
1748 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1749 flow[1].em.portnr := 20000;
1750 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], false);
1751
1752 /* The first leg starts transmitting */
1753 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1754 f_sleep(0.5);
1755 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1756 if (stats[0].num_pkts_rx_err_disabled != 0) {
1757 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001758 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001759 }
1760 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1761 if (stats[1].num_pkts_rx_err_disabled != 0) {
1762 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001763 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001764 }
1765
1766 /* The second leg starts transmitting a little later */
1767 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1768 f_sleep(1.0);
1769 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1770 if (stats[0].num_pkts_rx_err_disabled != 0) {
1771 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001772 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001773 }
1774 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1775 if (stats[1].num_pkts_rx_err_disabled != 0) {
1776 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001777 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001778 }
1779
1780 /* The first leg will now be switched into bidirectional
1781 * mode, but we do not expect any data comming back yet. */
1782 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1783 f_sleep(0.5);
1784 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1785 if (stats[1].num_pkts_rx_err_disabled != 0) {
1786 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001787 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001788 }
1789 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1790 if (stats[1].num_pkts_rx_err_disabled != 0) {
1791 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001792 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001793 }
1794
1795 /* When the second leg is switched into bidirectional mode
1796 * as well, then the MGW will connect the two together and
1797 * we should see RTP streams passing through from both ends. */
1798 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1799 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1800 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1801 num_pkts_tx[0] := stats[0].num_pkts_tx
1802 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1803 num_pkts_tx[1] := stats[1].num_pkts_tx
1804
1805 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1806 f_sleep(2.0);
1807
1808 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1809 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1810
1811 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1812 if (temp > 3 or temp < -3) {
1813 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001814 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001815 }
1816
1817 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1818 if (temp > 3 or temp < -3) {
1819 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001820 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001821 }
Philipp Maier36291392018-07-25 09:40:44 +02001822
1823 f_rtpem_stats_err_check(stats[0]);
1824 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001825
1826 /* Tear down */
1827 f_flow_delete(RTPEM[0]);
1828 f_flow_delete(RTPEM[1], ep, call_id);
1829 setverdict(pass);
1830 }
1831
1832 /* Test what happens when two RTP streams from different sources target
1833 * a single connection. Is the unsolicited stream properly ignored? */
1834 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1835 var RtpFlowData flow[2];
1836 var RtpemStats stats[2];
1837 var MgcpResponse resp;
1838 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1839 var MgcpCallId call_id := '1234321326'H;
1840 var integer unsolicited_port := 10002;
1841
1842 f_init(ep);
1843
1844 /* from us to MGW */
1845 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1846 /* bind local RTP emulation sockets */
1847 flow[0].em.portnr := 10000;
1848 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1849
1850 /* from MGW back to us */
1851 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1852 flow[1].em.portnr := 20000;
1853 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001854
1855 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1856 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1857
Philipp Maier2321ef92018-06-27 17:52:04 +02001858 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001859
Philipp Maier2321ef92018-06-27 17:52:04 +02001860 /* Start inserting unsolicited RTP packets */
Philipp Maier4de3e372018-06-29 17:15:22 +02001861 f_rtpem_bind(RTPEM[2], mp_local_ip, unsolicited_port);
1862 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001863 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1864
1865 f_sleep(0.5);
1866
Daniel Willmanna069d382018-12-13 13:53:33 +01001867 /* Stop transmitting packets and tear down the flows */
1868 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02001869 f_flow_delete(RTPEM[0]);
1870 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001871
1872 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1873 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1874 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1875 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001876 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001877 }
1878
Philipp Maier36291392018-07-25 09:40:44 +02001879 f_rtpem_stats_err_check(stats[0]);
1880 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02001881
Harald Weltebb7523b2018-03-29 08:52:01 +02001882 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001883 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001884
Philipp Maier2321ef92018-06-27 17:52:04 +02001885 /* Test a handover situation. We first create two connections transmit
1886 * some data bidirectionally. Then we will simulate a handover situation. */
1887 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1888 var RtpFlowData flow[2];
1889 var RtpemStats stats[3];
1890 var MgcpResponse resp;
1891 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1892 var MgcpCallId call_id := '76338'H;
1893 var integer port_old;
1894
1895 f_init(ep);
1896
1897 /* First connection (BTS) */
1898 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1899 /* bind local RTP emulation sockets */
1900 flow[0].em.portnr := 10000;
1901 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1902
1903 /* Second connection (PBX) */
1904 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1905 flow[1].em.portnr := 20000;
1906 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1907
1908 /* Normal rtp flow for one second */
1909 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1910 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1911 f_sleep(1.0);
1912
1913 /* Now switch the flow over to a new port (BTS) */
1914 port_old := flow[0].em.portnr;
1915 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001916 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02001917 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001918 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02001919
1920 /* When handing over a call, the old source may still keep
1921 * transmitting for a while. We simulate this by injecting
1922 * some unsolicited packets on the behalf of the old source,
1923 * (old remote port) */
Philipp Maier4de3e372018-06-29 17:15:22 +02001924 f_rtpem_bind(RTPEM[2], mp_local_ip, port_old);
1925 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001926 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1927 f_sleep(1.0);
1928 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
1929 f_sleep(1.0);
1930
1931 /* Terminate call */
1932 f_flow_delete(RTPEM[0]);
1933 f_flow_delete(RTPEM[1], ep, call_id);
1934
1935 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1936 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1937 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
1938 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001939 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001940 }
1941 stats[2] := f_rtpem_stats_get(RTPEM[2]);
1942 if (stats[2].num_pkts_rx_err_disabled != 0) {
1943 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02001944 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001945 }
1946
Philipp Maier36291392018-07-25 09:40:44 +02001947 f_rtpem_stats_err_check(stats[0]);
1948 f_rtpem_stats_err_check(stats[1]);
1949 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02001950
Philipp Maier2321ef92018-06-27 17:52:04 +02001951 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02001952 }
Harald Weltef53f1642017-11-18 19:57:11 +01001953
Philipp Maier6d4e0942019-02-21 17:35:01 +01001954
1955 /* create two local RTP emulations; create two connections on MGW EP, see if
1956 * exchanged data is converted bwtween ts101318 and rfc5993 */
1957 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
1958 var RtpFlowData flow[2];
1959 var RtpemStats stats[2];
1960 var MgcpResponse resp;
1961 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1962 var MgcpCallId call_id := '1226'H;
1963
1964 f_init(ep);
1965
1966 /* Turn on conversion mode */
1967 f_vty_enter_config(MGWVTY);
1968 f_vty_transceive(MGWVTY, "mgcp");
1969 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
1970
1971 /* from us to MGW */
1972 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1973 /* bind local RTP emulation sockets */
1974 flow[0].em.portnr := 10000;
1975 flow[0].rtp_cfg := c_RtpemDefaultCfg;
1976 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1977 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1978 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1979 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1980
1981 /* from MGW back to us */
1982 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1983 flow[1].em.portnr := 20000;
1984 flow[1].rtp_cfg := c_RtpemDefaultCfg;
1985 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
1986 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1987 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1988 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1989
1990 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1991 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1992
1993 f_sleep(1.0);
1994
1995 f_flow_delete(RTPEM[0]);
1996 f_flow_delete(RTPEM[1], ep, call_id);
1997
1998 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1999 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2000
2001 f_rtpem_stats_err_check(stats[0]);
2002 f_rtpem_stats_err_check(stats[1]);
2003
2004 /* Turn off conversion mode */
2005 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2006
2007 setverdict(pass);
2008 }
2009
Philipp Maier4f764ce2019-03-07 10:54:10 +01002010 /* create two local RTP emulations; create two connections on MGW EP, see if
2011 * exchanged data is converted between AMR octet-aligned and bandwith
2012 * efficient-mode */
2013 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2014 var RtpFlowData flow[2];
2015 var RtpemStats stats[2];
2016 var MgcpResponse resp;
2017 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2018 var MgcpCallId call_id := '1226'H;
2019
2020 f_init(ep);
2021
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002022 /* Connection #0 (Bidirectional) */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002023 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
2024 /* bind local RTP emulation sockets */
2025 flow[0].em.portnr := 10000;
2026 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2027 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2028 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2029 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2030 flow[0].fmtp := fmtp0;
2031 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2032
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002033 /* Connection #1 (Bidirectional) */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002034 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
2035 flow[1].em.portnr := 20000;
2036 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2037 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2038 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2039 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2040 flow[1].fmtp := fmtp1;
2041 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2042
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002043 /* Send RTP packets to connection #0, receive on connection #1 */
2044 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2045 f_sleep(0.5);
2046 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002047 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002048 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2049 f_sleep(0.5);
2050 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002051
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002052 /* Send RTP packets to connection #1, receive on connection #0 */
2053 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2054 f_sleep(0.5);
2055 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2056 f_sleep(1.0);
2057 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2058 f_sleep(0.5);
2059 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2060
2061 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002062 f_flow_delete(RTPEM[0]);
2063 f_flow_delete(RTPEM[1], ep, call_id);
2064
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002065 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002066 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2067 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002068 f_rtpem_stats_err_check(stats[0]);
2069 f_rtpem_stats_err_check(stats[1]);
2070
2071 setverdict(pass);
2072 }
2073
2074 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
2075 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=1", "octet-align=0");
2076 }
2077
2078 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2079 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2080 }
2081
2082 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2083 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2084 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002085
Harald Weltee636afd2017-09-17 16:24:09 +08002086 /* TODO: Double-DLCX (no retransmission) */
2087
2088
2089
2090 /* TODO: AUEP (various) */
2091 /* TODO: RSIP (various) */
2092 /* TODO: RQNT (various) */
2093 /* TODO: EPCF (various) */
2094 /* TODO: AUCX (various) */
2095 /* TODO: invalid verb (various) */
2096
Oliver Smith021141e2019-06-25 12:09:01 +02002097
2098 testcase TC_conn_timeout() runs on dummy_CT {
2099 var RtpFlowData flow;
2100 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2101 var MgcpCallId call_id := '1225'H;
2102 var MGCP_RecvFrom mrf;
2103
2104 f_init(ep);
2105 log("Setting conn-timeout to 1s");
2106 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2107
2108 log("Sending RTP data for 1.5s");
2109 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
2110 flow.em.portnr := 10000;
2111 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2112 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2113 f_sleep(1.5);
2114
2115 log("Stopping for 0.5s and resuming");
2116 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2117 f_sleep(0.5);
2118 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2119 f_sleep(0.1);
2120
2121 log("Stopping for 1.5s, expecting to run into timeout");
2122 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2123 f_sleep(1.5);
2124
2125 log("Resuming should fail now");
2126 f_rtpem_conn_refuse_expect(RTPEM[0]);
2127 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2128 f_sleep(0.2);
2129 f_rtpem_conn_refuse_verify(RTPEM[0]);
2130
2131 setverdict(pass);
2132 }
2133
Harald Welte00a067f2017-09-13 23:27:17 +02002134 control {
2135 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002136 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002137 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002138 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002139 execute(TC_crcx_unsupp_mode());
2140 execute(TC_crcx_early_bidir_mode());
2141 execute(TC_crcx_unsupp_param());
2142 execute(TC_crcx_missing_callid());
2143 execute(TC_crcx_missing_mode());
2144 execute(TC_crcx_unsupp_packet_intv());
2145 execute(TC_crcx_illegal_double_lco());
2146 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002147 execute(TC_crcx_wildcarded());
2148 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002149 execute(TC_mdcx_without_crcx());
2150 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002151 execute(TC_mdcx_wildcarded());
2152 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002153 execute(TC_crcx_and_dlcx_ep_callid_connid());
2154 execute(TC_crcx_and_dlcx_ep_callid());
2155 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002156 execute(TC_crcx_and_dlcx_ep_callid_inval());
2157 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002158 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002159
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002160 execute(TC_crcx_osmux_wildcard());
2161 execute(TC_crcx_osmux_fixed());
2162 execute(TC_crcx_osmux_fixed_twice());
2163 execute(TC_one_crcx_receive_only_osmux());
2164 execute(TC_one_crcx_loopback_osmux());
2165 execute(TC_two_crcx_and_rtp_osmux());
2166 execute(TC_two_crcx_and_rtp_osmux_bidir());
2167 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2168 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2169
Harald Welte33d82162017-12-28 03:21:57 +01002170 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002171
2172 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002173
2174 execute(TC_one_crcx_receive_only_rtp());
2175 execute(TC_one_crcx_loopback_rtp());
Harald Weltebb7523b2018-03-29 08:52:01 +02002176 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002177 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002178 execute(TC_two_crcx_diff_pt_and_rtp());
2179 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002180 execute(TC_two_crcx_mdcx_and_rtp());
2181 execute(TC_two_crcx_and_unsolicited_rtp());
2182 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002183 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002184 execute(TC_amr_oa_bwe_rtp_conversion());
2185 execute(TC_amr_oa_oa_rtp_conversion());
2186 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002187
Oliver Smith280536b2019-06-28 11:04:48 +02002188 if (mp_enable_conn_timeout_test) {
2189 execute(TC_conn_timeout());
2190 }
Harald Welte00a067f2017-09-13 23:27:17 +02002191 }
2192}