blob: 77eed4b8511de0a744870afabe0670d40cb6af0a [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;
Harald Welte3c6ebb92017-09-16 00:56:57 +080072 }
73
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020074 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
75 /* Turn on conversion mode */
76 f_vty_enter_config(MGWVTY);
77 f_vty_transceive(MGWVTY, "mgcp");
78 if (osmux_on) {
79 f_vty_transceive(MGWVTY, "osmux on");
80 } else {
81 f_vty_transceive(MGWVTY, "osmux off");
82 }
83 f_vty_transceive(MGWVTY, "exit");
84 f_vty_transceive(MGWVTY, "exit");
85
86 }
87
88 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +010089 map(self:MGWVTY, system:MGWVTY);
90 f_vty_set_prompts(MGWVTY);
91 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020092
93 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +010094 }
95
Harald Weltebb7523b2018-03-29 08:52:01 +020096 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
97 runs on dummy_CT {
98 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
99 map(comp_ref:RTP, system:RTP);
100 map(comp_ref:RTCP, system:RTCP);
101 comp_ref.start(RTP_Emulation.f_main());
102 }
103
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200104 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
105 runs on dummy_CT {
106 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
107 map(comp_ref:OSMUX, system:OSMUX);
108 comp_ref.start(OSMUX_Emulation.f_main());
109 }
110
Harald Welte21ba5572017-09-19 17:55:05 +0800111 /* initialization function, called by each test case at the
112 * beginning, but 'initialized' variable ensures its body is
113 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200114 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800115 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100116 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200117
Harald Welteedc45c12017-11-18 19:15:05 +0100118 if (initialized == false) {
119 initialized := true;
120
121 /* some random number for the initial transaction id */
122 g_trans_id := float2int(rnd()*65535.0);
123 map(self:MGCP, system:MGCP_CODEC_PT);
124 /* connect the MGCP test port using the given
125 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
126 * */
127 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 +0200128 if (not ispresent(res.connId)) {
129 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200130 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200131 }
Harald Welteedc45c12017-11-18 19:15:05 +0100132 g_mgcp_conn_id := res.connId;
133
Harald Weltebb7523b2018-03-29 08:52:01 +0200134 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
135 f_rtpem_init(vc_RTPEM[i], i);
136 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
137 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200138 if (osmux_on) {
139 f_osmuxem_init(vc_OsmuxEM);
140 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
141 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800142 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800143
Harald Welteedc45c12017-11-18 19:15:05 +0100144 if (isvalue(ep)) {
145 /* do a DLCX on all connections of the EP */
146 f_dlcx_ignore(valueof(ep));
147 }
Philipp Maier6137c322019-02-20 16:13:41 +0100148
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200149 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800150 }
151
Harald Welte00a067f2017-09-13 23:27:17 +0200152 testcase TC_selftest() runs on dummy_CT {
153 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 +0100154 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 +0200155 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
156 "I: 1\n" &
157 "\n" &
158 "v=0\r\n" &
159 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
160 "s=-\r\n" &
161 "c=IN IP4 0.0.0.0\r\n" &
162 "t=0 0\r\n" &
163 "m=audio 0 RTP/AVP 126\r\n" &
164 "a=rtpmap:126 AMR/8000\r\n" &
165 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100166 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 +0200167 "M: sendrecv\r" &
168 "C: 2\r\n" &
169 "I: 1\r\n" &
170 "L: p:20, a:AMR, nt:IN\r\n" &
171 "\n" &
172 "v=0\r\n" &
173 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800174 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200175 "c=IN IP4 0.0.0.0\r\n" &
176 "t=0 0\r\n" &
177 "m=audio 4441 RTP/AVP 99\r\n" &
178 "a=rtpmap:99 AMR/8000\r\n" &
179 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800180 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200181
182 log(c_auep);
183 log(dec_MgcpCommand(c_auep));
184
185 log(c_mdcx3);
186 log(dec_MgcpCommand(c_mdcx3));
187
188 log(c_mdcx3_ret);
189 log(dec_MgcpResponse(c_mdcx3_ret));
190
191 log(c_mdcx4);
192 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800193
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100194 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
195 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 +0800196
197 log(c_crcx510_ret);
198 log(dec_MgcpResponse(c_crcx510_ret));
199 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100200
201 /* We didn't encounter any DTE, so pass the test */
202 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800203 }
204
Harald Weltee636afd2017-09-17 16:24:09 +0800205 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100206 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800207 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
208 * - CRCX with remote session description and without
209 *
210 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100211 * x packetization != 20ms
212 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800213 * x unsupported mode (517)
214 * x bidirectional mode before RemoteConnDesc: 527
215 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100216 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800217 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
218 */
219
Harald Welte21ba5572017-09-19 17:55:05 +0800220 /* build a receive template for receiving a MGCP message. You
221 * pass the MGCP response template in, and it will generate an
222 * MGCP_RecvFrom template that can match the primitives arriving on the
223 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800224 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
225 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100226 connId := g_mgcp_conn_id,
Harald Weltee636afd2017-09-17 16:24:09 +0800227 remName := mp_remote_ip,
228 remPort := mp_remote_udp_port,
229 locName := mp_local_ip,
230 locPort := mp_local_udp_port,
231 msg := { response := resp }
232 }
233 return mrf;
234 }
235
236 /* Send a MGCP request + receive a (matching!) response */
237 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
238 var MgcpMessage msg := { command := valueof(cmd) };
239 resp.line.trans_id := cmd.line.trans_id;
240 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800241 var MGCP_RecvFrom mrf;
242 timer T := 5.0;
243
Harald Welte55015362017-11-18 16:02:42 +0100244 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800245 T.start;
246 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800247 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200248 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
249 setverdict(fail, "Response didn't match template");
250 mtc.stop;
251 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800252 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200253 [] T.timeout {
254 setverdict(fail, "Timeout waiting for response to ", cmd);
255 mtc.stop;
256 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800257 }
258 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800259
260 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
261 return mrf.msg.response;
262 } else {
263 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
264 return r;
265 }
Harald Welte00a067f2017-09-13 23:27:17 +0200266 }
267
Harald Welteba62c8c2017-11-18 18:26:49 +0100268 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
269 var integer i;
270 for (i := 0; i < lengthof(resp.params); i := i + 1) {
271 var MgcpParameter par := resp.params[i];
272 if (par.code == "I") {
273 return str2hex(par.val);
274 }
275 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200276 setverdict(fail, "Could not find conn id for MgcpReponse");
277 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100278 return '00000000'H;
279 }
280
Harald Welte10889c12017-11-18 19:40:31 +0100281 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
282 template MgcpCallId call_id := omit,
283 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100284 var template MgcpCommand cmd;
285 var MgcpResponse resp;
286 var template MgcpResponse rtmpl := {
287 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100288 code := ret_code,
289 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100290 },
291 params := *,
292 sdp := *
293 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100294 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100295 resp := mgcp_transceive_mgw(cmd, rtmpl);
296 }
297
Harald Welte10889c12017-11-18 19:40:31 +0100298 /* Send DLCX and expect OK response */
299 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
300 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100301 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100302 }
303
Harald Welteba62c8c2017-11-18 18:26:49 +0100304 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100305 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100306 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100307 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100308 }
309
Harald Weltebb7523b2018-03-29 08:52:01 +0200310 type record HostPort {
311 charstring hostname,
312 integer portnr optional
313 }
314 type record RtpFlowData {
315 HostPort em, /* emulation side */
316 HostPort mgw, /* mgw side */
317 uint7_t pt,
318 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200319 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100320 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200321 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
322 MgcpOsmuxCID osmux_cid optional,
323 MgcpOsmuxCID osmux_cid_response optional,
324 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100325 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200326 }
327
Philipp Maier2321ef92018-06-27 17:52:04 +0200328 /* Create an RTP flow (bidirectional, or receive-only) */
329 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 +0200330 boolean one_phase := true)
331 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200332 var template MgcpCommand cmd;
333 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100334 var SDP_attribute_list attributes;
335
336 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
337 if (isvalue(flow.fmtp)) {
338 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
339 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200340
341 /* bind local RTP emulation socket */
342 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
343
Philipp Maier28bb8292018-07-20 17:09:17 +0200344 /* configure rtp-emulation */
345 if (ispresent(flow.rtp_cfg)) {
346 f_rtpem_configure(pt, flow.rtp_cfg);
347 } else {
348 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
349 rtp_cfg.tx_payload_type := flow.pt
350 f_rtpem_configure(pt, rtp_cfg);
351 }
352
Harald Weltebb7523b2018-03-29 08:52:01 +0200353 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200354 /* Connect flow to MGW using a CRCX that also contains an SDP
355 * part that tells the MGW where we are listening for RTP streams
356 * that come from the MGW. We get a fully working connection in
357 * one go. */
358
359 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200360 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100361 flow.em.portnr, { int2str(flow.pt) }, attributes);
362
Harald Weltebb7523b2018-03-29 08:52:01 +0200363 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
364 flow.mgcp_conn_id := extract_conn_id(resp);
365 /* extract port number from response */
366 flow.mgw.portnr :=
367 resp.sdp.media_list[0].media_field.ports.port_number;
368 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200369 /* Create a half-open connection only. We do not tell the MGW
370 * where it can send RTP streams to us. This means this
371 * connection will only be able to receive but can not send
372 * data back to us. In order to turn the connection in a fully
373 * bi-directional one, a separate MDCX is needed. */
374
375 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200376 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
377 flow.mgcp_conn_id := extract_conn_id(resp);
378 /* extract MGW-side port number from response */
379 flow.mgw.portnr :=
380 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200381 }
382 /* finally, connect the emulation-side RTP socket to the MGW */
383 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
384 }
385
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200386 /* Create an Osmux flow (bidirectional, or receive-only) */
387 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
388 boolean one_phase := true)
389 runs on dummy_CT {
390 var template MgcpCommand cmd;
391 var MgcpResponse resp;
392 var SDP_attribute_list attributes;
393 var OsmuxTxHandle tx_hdl;
394 var OsmuxRxHandle rx_hdl;
395 var charstring cid_response;
396 var OsmuxCID cid_resp_parsed
397
398 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
399 if (isvalue(flow.fmtp)) {
400 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
401 }
402
403 /* bind local Osmux emulation socket */
404 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
405
406 /* configure osmux-emulation */
407 if (ispresent(flow.osmux_cfg)) {
408 f_osmuxem_configure(pt, flow.osmux_cfg);
409 } else {
410 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
411 f_osmuxem_configure(pt, osmux_cfg);
412 flow.osmux_cfg := osmux_cfg
413 }
414
415 if (one_phase) {
416 /* Connect flow to MGW using a CRCX that also contains an SDP
417 * part that tells the MGW where we are listening for Osmux streams
418 * that come from the MGW. We get a fully working connection in
419 * one go. */
420 rx_hdl := c_OsmuxemDefaultRxHandle;
421 rx_hdl.cid := flow.osmux_cid;
422 f_osmuxem_register_rxhandle(pt, rx_hdl);
423 flow.osmux_cid_sent := true;
424 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
425 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
426 flow.em.portnr, { int2str(flow.pt) }, attributes);
427 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
428 flow.mgcp_conn_id := extract_conn_id(resp);
429 /* extract port number from response */
430 flow.mgw.portnr :=
431 resp.sdp.media_list[0].media_field.ports.port_number;
432 } else {
433 /* Create a half-open connection only. We do not tell the MGW
434 * where it can send Osmux streams to us. This means this
435 * connection will only be able to receive but can not send
436 * data back to us. In order to turn the connection in a fully
437 * bi-directional one, a separate MDCX is needed. */
438
439 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
440 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
441
442 flow.mgcp_conn_id := extract_conn_id(resp);
443 /* extract MGW-side port number from response */
444 flow.mgw.portnr :=
445 resp.sdp.media_list[0].media_field.ports.port_number;
446 }
447
448 /* extract Osmux CID we got assigned by the MGW */
449 var MgcpMessage resp_msg := {
450 response := resp
451 }
452
453 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
454 setverdict(fail, "No Osmux CID in MGCP response", resp);
455 mtc.stop;
456 }
457
458 /* Make sure response is no wildcard */
459 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
460 if (flow.osmux_cid_response == -1) {
461 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
462 mtc.stop;
463 }
464 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
465 f_osmuxem_register_txhandle(pt, tx_hdl);
466
467 /* finally, connect the emulation-side RTP socket to the MGW */
468 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
469 }
470
Philipp Maier2321ef92018-06-27 17:52:04 +0200471 /* Modify an existing RTP flow */
472 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
473 runs on dummy_CT {
474 var template MgcpCommand cmd;
475 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100476 var SDP_attribute_list attributes;
477
478 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
479 if (isvalue(flow.fmtp)) {
480 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
481 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200482
483 /* rebind local RTP emulation socket to the new address */
484 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
485
Philipp Maier28bb8292018-07-20 17:09:17 +0200486 /* reconfigure rtp-emulation */
487 if (ispresent(flow.rtp_cfg)) {
488 f_rtpem_configure(pt, flow.rtp_cfg);
489 } else {
490 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
491 rtp_cfg.tx_payload_type := flow.pt
492 f_rtpem_configure(pt, rtp_cfg);
493 }
494
Philipp Maier2321ef92018-06-27 17:52:04 +0200495 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
496 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
497 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100498 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200499 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
500
501 /* extract MGW-side port number from response. (usually this
502 * will not change, but thats is up to the MGW) */
503 flow.mgw.portnr :=
504 resp.sdp.media_list[0].media_field.ports.port_number;
505
506 /* reconnect the emulation-side RTP socket to the MGW */
507 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
508 }
509
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200510 /* Modify an existing Osmux flow */
511 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
512 runs on dummy_CT {
513 var template MgcpCommand cmd;
514 var MgcpResponse resp;
515 var SDP_attribute_list attributes;
516 var OsmuxRxHandle rx_hdl;
517 var charstring cid_response;
518 var OsmuxCID cid_resp_parsed
519
520 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
521 if (isvalue(flow.fmtp)) {
522 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
523 }
524
525 /* rebind local Osmux emulation socket to the new address */
526 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
527
528 /* configure osmux-emulation */
529 if (ispresent(flow.osmux_cfg)) {
530 f_osmuxem_configure(pt, flow.osmux_cfg);
531 } else {
532 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
533 f_osmuxem_configure(pt, osmux_cfg);
534 }
535
536 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
537 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
538 rx_hdl := c_OsmuxemDefaultRxHandle;
539 rx_hdl.cid := flow.osmux_cid;
540 f_osmuxem_register_rxhandle(pt, rx_hdl);
541 flow.osmux_cid_sent := true;
542 }
543
544 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
545 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
546 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
547 flow.em.portnr, { int2str(flow.pt) }, attributes);
548 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
549
550 /* extract MGW-side port number from response. (usually this
551 * will not change, but thats is up to the MGW) */
552 flow.mgw.portnr :=
553 resp.sdp.media_list[0].media_field.ports.port_number;
554
555 /* extract Osmux CID we got assigned by the MGW */
556 var MgcpMessage resp_msg := {
557 response := resp
558 }
559
560 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
561 setverdict(fail, "No Osmux CID in MGCP response", resp);
562 mtc.stop;
563 }
564
565 /* Make sure response is no wildcard */
566 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
567 if (cid_resp_parsed == -1) {
568 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
569 mtc.stop;
570 }
571 if (cid_resp_parsed != flow.osmux_cid_response) {
572 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
573 mtc.stop;
574 }
575
576 /* reconnect the emulation-side Osmux socket to the MGW */
577 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
578 }
579
Philipp Maier2321ef92018-06-27 17:52:04 +0200580 /* Delete an existing RTP flow */
581 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
582 runs on dummy_CT {
583 var template MgcpCommand cmd;
584 var MgcpResponse resp;
585
586 /* Switch off RTP flow */
587 f_rtpem_mode(pt, RTPEM_MODE_NONE);
588
589 /* Delete connection on MGW (if needed) */
590 if (isvalue(call_id) and isvalue(ep)) {
591 f_sleep(0.1);
592 f_dlcx_ok(valueof(ep), call_id);
593 }
594 }
595
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200596 /* Delete an existing Osmux flow */
597 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
598 runs on dummy_CT {
599 var template MgcpCommand cmd;
600 var MgcpResponse resp;
601
602 /* Switch off Osmux flow */
603 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
604
605 /* Delete connection on MGW (if needed) */
606 if (isvalue(call_id) and isvalue(ep)) {
607 f_sleep(0.1);
608 f_dlcx_ok(valueof(ep), call_id);
609 }
610 }
611
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100612 function f_crcx(charstring ep_prefix) runs on dummy_CT {
613 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800614 var template MgcpCommand cmd;
615 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100616 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800617
Harald Welteedc45c12017-11-18 19:15:05 +0100618 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800619
Harald Welteba62c8c2017-11-18 18:26:49 +0100620 /* create the connection on the MGW */
621 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100622 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100623 extract_conn_id(resp);
624
625 /* clean-up */
626 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100627 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100628
Philipp Maier45635f42018-06-05 17:28:02 +0200629 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
630 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
631 var template MgcpCommand cmd;
632 var MgcpResponse resp;
633 var MgcpCallId call_id := '1234'H;
634
635 f_init(ep);
636
637 /* create the connection on the MGW */
638 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
639 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
640 extract_conn_id(resp);
641
642 /* clean-up */
643 f_dlcx_ok(ep, call_id);
644
645 /* See also OS#2658: Even when we omit the LCO information, we
646 expect the MGW to pick a sane payload type for us. This
647 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200648 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200649 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200650 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200651 }
652
653 /* See also OS#2658: We also expect the MGW to assign a port
654 number to us. */
655 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
656 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200657 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200658 }
659 }
660
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200661 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
662 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
663 var template MgcpCommand cmd;
664 var MgcpResponse resp;
665 var MgcpCallId call_id := '1234'H;
666 var charstring cid_response;
667
668 if (run_init) {
669 f_init(ep, true);
670 }
671
672 /* create the connection on the MGW */
673 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
674 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
675 extract_conn_id(resp);
676
677 /* extract Osmux CID we got assigned by the MGW */
678 var MgcpMessage resp_msg := {
679 response := resp
680 }
681
682 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
683 setverdict(fail, "No Osmux CID in MGCP response", resp);
684 mtc.stop;
685 }
686
687 /* Make sure response is no wildcard */
688 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
689 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
690 mtc.stop;
691 }
692
693 /* clean-up */
694 f_dlcx_ok(ep, call_id);
695 }
696
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100697 /* test valid CRCX without SDP */
698 testcase TC_crcx() runs on dummy_CT {
699 f_crcx(c_mgw_ep_rtpbridge);
700 setverdict(pass);
701 }
702
Philipp Maier45635f42018-06-05 17:28:02 +0200703 /* test valid CRCX without SDP and LCO */
704 testcase TC_crcx_no_lco() runs on dummy_CT {
705 f_crcx_no_lco(c_mgw_ep_rtpbridge);
706 setverdict(pass);
707 }
708
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100709 /* test valid CRCX without SDP (older method without endpoint prefix) */
710 testcase TC_crcx_noprefix() runs on dummy_CT {
711 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800712 setverdict(pass);
713 }
714
715 /* test CRCX with unsupported mode, expect 517 */
716 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
717 var template MgcpCommand cmd;
718 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100719 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100720 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800721 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
722
Harald Welteedc45c12017-11-18 19:15:05 +0100723 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800724
Harald Welteba62c8c2017-11-18 18:26:49 +0100725 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800726 resp := mgcp_transceive_mgw(cmd, rtmpl);
727 setverdict(pass);
728 }
729
Harald Welte21ba5572017-09-19 17:55:05 +0800730 /* test CRCX with early bi-directional mode, expect 527 as
731 * bi-diretional media can only be established once both local and
732 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800733 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
734 var template MgcpCommand cmd;
735 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100736 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100737 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800738 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
739
Harald Welteedc45c12017-11-18 19:15:05 +0100740 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800741
Harald Welteba62c8c2017-11-18 18:26:49 +0100742 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800743 resp := mgcp_transceive_mgw(cmd, rtmpl);
744 setverdict(pass);
745 }
746
747 /* test CRCX with unsupported Parameters */
748 testcase TC_crcx_unsupp_param() runs on dummy_CT {
749 var template MgcpCommand cmd;
750 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100751 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100752 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800753 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
754
Harald Welteedc45c12017-11-18 19:15:05 +0100755 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800756
Harald Welteba62c8c2017-11-18 18:26:49 +0100757 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100758 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
759 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
760
Harald Weltee636afd2017-09-17 16:24:09 +0800761 resp := mgcp_transceive_mgw(cmd, rtmpl);
762 setverdict(pass);
763 }
764
765 /* test CRCX with missing CallId */
766 testcase TC_crcx_missing_callid() runs on dummy_CT {
767 var template MgcpCommand cmd;
768 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100769 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100770 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800771
Harald Welteedc45c12017-11-18 19:15:05 +0100772 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800773
Harald Welteba62c8c2017-11-18 18:26:49 +0100774 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800775 cmd.params := {
776 t_MgcpParConnMode("recvonly"),
777 t_MgcpParLocConnOpt("p:20")
778 }
779 resp := mgcp_transceive_mgw(cmd, rtmpl);
780 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100781
Harald Weltee636afd2017-09-17 16:24:09 +0800782 }
783
784 /* test CRCX with missing Mode */
785 testcase TC_crcx_missing_mode() runs on dummy_CT {
786 var template MgcpCommand cmd;
787 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100788 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100789 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100790 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800791
Harald Welteedc45c12017-11-18 19:15:05 +0100792 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800793
Harald Welteba62c8c2017-11-18 18:26:49 +0100794 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800795 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100796 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800797 t_MgcpParLocConnOpt("p:20")
798 }
799 resp := mgcp_transceive_mgw(cmd, rtmpl);
800 setverdict(pass);
801 }
802
803 /* test CRCX with unsupported packetization interval */
804 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
805 var template MgcpCommand cmd;
806 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100807 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100808 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100809 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800810
Harald Welteedc45c12017-11-18 19:15:05 +0100811 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800812
Harald Welteba62c8c2017-11-18 18:26:49 +0100813 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100814 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800815 resp := mgcp_transceive_mgw(cmd, rtmpl);
816 setverdict(pass);
817 }
818
819 /* test CRCX with illegal double presence of local connection option */
820 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
821 var template MgcpCommand cmd;
822 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100823 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100824 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800825 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
826
Harald Welteedc45c12017-11-18 19:15:05 +0100827 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800828
Harald Welteba62c8c2017-11-18 18:26:49 +0100829 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100830 /* p:20 is permitted only once and not twice! */
831 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800832 resp := mgcp_transceive_mgw(cmd, rtmpl);
833 setverdict(pass);
834 }
835
836 /* test valid CRCX with valid SDP */
837 testcase TC_crcx_sdp() runs on dummy_CT {
838 var template MgcpCommand cmd;
839 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100840 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100841 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800842
Harald Welteedc45c12017-11-18 19:15:05 +0100843 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800844
Harald Welteba62c8c2017-11-18 18:26:49 +0100845 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800846 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
847 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
848 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100849 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100850
851 /* clean-up */
852 f_dlcx_ok(ep, call_id);
853
Harald Weltee636afd2017-09-17 16:24:09 +0800854 setverdict(pass);
855 }
856
Philipp Maier5e06cee2018-02-01 18:28:08 +0100857 /* test valid wildcarded CRCX */
858 testcase TC_crcx_wildcarded() runs on dummy_CT {
859 var template MgcpCommand cmd;
860 var MgcpResponse resp;
861 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
862 var MgcpCallId call_id := '1234'H;
863 var MgcpEndpoint ep_assigned;
864 f_init();
865
866 /* create the connection on the MGW */
867 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
868 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
869 extract_conn_id(resp);
870
871 /* extract endpoint name we got assigned by the MGW */
872 var MgcpMessage resp_msg := {
873 response := resp
874 }
875 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
876 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200877 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100878 }
879
880 /* clean-up */
881 f_dlcx_ok(ep_assigned, call_id);
882
883 setverdict(pass);
884 }
885
886 /* test valid wildcarded CRCX */
887 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
888 const integer n_endpoints := 32;
889 var integer i;
890 var template MgcpCommand cmd;
891 var MgcpResponse resp;
892 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
893 var MgcpCallId call_id := '1234'H;
894 var MgcpEndpoint ep_assigned[n_endpoints];
895 f_init();
896
897 /* Exhaust all endpoint resources on the virtual trunk */
898 for (i := 0; i < n_endpoints; i := i+1) {
899 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
900 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
901
902 /* Make sure we got a connection id */
903 extract_conn_id(resp);
904
905 var MgcpMessage resp_msg := {
906 response := resp
907 }
908 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
909 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200910 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100911 }
912 }
913
914 /* Try to allocate one more endpoint, which should fail */
915 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
916 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
917 resp := mgcp_transceive_mgw(cmd, rtmpl);
918 setverdict(pass);
919
920 /* clean-up */
921 for (i := 0; i < n_endpoints; i := i+1) {
922 f_dlcx_ok(ep_assigned[i], call_id);
923 }
924 setverdict(pass);
925 }
926
Harald Weltee636afd2017-09-17 16:24:09 +0800927 /* TODO: various SDP related bits */
928
929
930 /* TODO: CRCX with X-Osmux */
931 /* TODO: double CRCX without force_realloc */
932
933 /* TODO: MDCX (various) */
934
935 /* TODO: MDCX without CRCX first */
936 testcase TC_mdcx_without_crcx() runs on dummy_CT {
937 var template MgcpCommand cmd;
938 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100939 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100940 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800941 var template MgcpResponse rtmpl := {
942 line := {
943 /* TODO: accept/enforce better error? */
944 code := "400",
945 string := ?
946 },
947 params:= { },
948 sdp := omit
949 };
950
Harald Welteedc45c12017-11-18 19:15:05 +0100951 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800952
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100953 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800954 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
955 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
956 valueof(ts_SDP_ptime(20)) });
957 resp := mgcp_transceive_mgw(cmd, rtmpl);
958 setverdict(pass);
959 }
960
961 /* DLCX without CRCX first */
962 testcase TC_dlcx_without_crcx() runs on dummy_CT {
963 var template MgcpCommand cmd;
964 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100965 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800966 var template MgcpResponse rtmpl := {
967 line := {
Harald Weltef91edf32017-12-28 14:16:21 +0100968 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +0800969 string := ?
970 },
971 params:= { },
972 sdp := omit
973 };
974
Harald Welteedc45c12017-11-18 19:15:05 +0100975 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800976
Harald Welteedc45c12017-11-18 19:15:05 +0100977 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800978 resp := mgcp_transceive_mgw(cmd, rtmpl);
979 setverdict(pass);
980 }
981
Philipp Maier8a3dc922018-02-02 14:55:12 +0100982 /* test valid wildcarded MDCX */
983 testcase TC_mdcx_wildcarded() runs on dummy_CT {
984 /* Note: A wildcarded MDCX is not allowed, so we expect the
985 * MGW to reject this request */
986 var template MgcpCommand cmd;
987 var MgcpResponse resp;
988 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
989 var MgcpCallId call_id := '1225'H;
990 var template MgcpResponse rtmpl := {
991 line := {
992 /* TODO: accept/enforce better error? */
993 code := "507",
994 string := ?
995 },
996 params:= { },
997 sdp := omit
998 };
999
1000 f_init(ep);
1001
1002 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1003 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1004 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1005 valueof(ts_SDP_ptime(20)) });
1006 resp := mgcp_transceive_mgw(cmd, rtmpl);
1007 setverdict(pass);
1008 }
1009
1010 /* test valid wildcarded DLCX */
1011 testcase TC_dlcx_wildcarded() runs on dummy_CT {
1012 /* Note: A wildcarded DLCX is specified, but our MGW does not
1013 * support this feature so we expect the MGW to reject the
1014 * request */
1015 var template MgcpCommand cmd;
1016 var MgcpResponse resp;
1017 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1018 var template MgcpResponse rtmpl := {
1019 line := {
1020 code := "507",
1021 string := ?
1022 },
1023 params:= { },
1024 sdp := omit
1025 };
1026
1027 f_init(ep);
1028
1029 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1030 resp := mgcp_transceive_mgw(cmd, rtmpl);
1031 setverdict(pass);
1032 }
1033
Harald Welte79181ff2017-11-18 19:26:11 +01001034 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1035 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1036 var template MgcpCommand cmd;
1037 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001038 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001039 var MgcpCallId call_id := '51234'H;
1040
1041 f_init(ep);
1042
1043 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1044 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1045
1046 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1047
1048 setverdict(pass);
1049 }
1050
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001051 /* test valid CRCX without SDP */
1052 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1053 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1054 setverdict(pass);
1055 }
1056
1057 /* test valid CRCX without SDP */
1058 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1059 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1060 setverdict(pass);
1061 }
1062
1063 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1064 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1065 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1066 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1067 setverdict(pass);
1068 }
1069
1070 /* Create one half open connection in receive-only mode. The MGW must accept
1071 * the packets but must not send any. */
1072 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1073 var RtpFlowData flow;
1074 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1075 var MgcpCallId call_id := '1225'H;
1076 var OsmuxemStats stats;
1077 var OsmuxTxHandle tx_hdl;
1078
1079 f_init(ep, true);
1080 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
1081 flow.em.portnr := mp_local_osmux_port;
1082 flow.osmux_cid := -1;
1083 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1084
1085 /* create a transmitter not yet known by MGW */
1086 tx_hdl := valueof(t_TxHandleAMR590(2));
1087 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1088
1089 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1090 f_sleep(1.0);
1091 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1092
1093 stats := f_osmuxem_stats_get(OsmuxEM);
1094
1095 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1096 setverdict(fail);
1097 }
1098 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1099 setverdict(fail);
1100 }
1101
1102 f_osmuxem_stats_err_check(stats);
1103
1104 setverdict(pass);
1105 }
1106
1107 /* Create one connection in loopback mode, test if the Osmux packets are
1108 * actually reflected */
1109 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1110 var RtpFlowData flow;
1111 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1112 var MgcpCallId call_id := '1225'H;
1113 var OsmuxemStats stats;
1114 var OsmuxTxHandle tx_hdl;
1115
1116 f_init(ep, true);
1117 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1118 flow.em.portnr := mp_local_osmux_port;
1119 flow.osmux_cid := 2;
1120 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1121
1122 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1123 f_sleep(1.0);
1124
1125 /* Switch off both Tx, wait to receive delayed frames from MGW */
1126 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1127 f_sleep(0.1);
1128 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1129
1130 stats := f_osmuxem_stats_get(OsmuxEM);
1131
1132 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1133 setverdict(fail);
1134 }
1135 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1136 setverdict(fail);
1137 }
1138
1139 f_osmuxem_stats_err_check(stats);
1140
1141 setverdict(pass);
1142 }
1143
1144 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1145 * must match the reception statistics on the other side and vice versa. The
1146 * user may also supply a tolerance value (number of packets) when deviations
1147 * are acceptable */
1148 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1149 var integer plen;
1150
1151 log("stats A: ", a);
1152 log("stats B: ", b);
1153 log("tolerance: ", tolerance, " packets");
1154 log("batch_size: ", batch_size, " packets");
1155
1156 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1157
1158 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1159 return false;
1160 }
1161
1162 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1163 return false;
1164 }
1165
1166 if(a.num_pkts_tx > 0) {
1167 plen := a.bytes_payload_tx / a.num_pkts_tx;
1168 } else {
1169 plen := 0;
1170 }
1171
1172 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1173 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1174 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1175 return false;
1176 }
1177
1178 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) {
1179 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1180 return false;
1181 }
1182
1183 return true;
1184 }
1185
1186 function f_TC_two_crcx_and_rtp_osmux(boolean bidir) runs on dummy_CT {
1187 var RtpFlowData flow[2];
1188 var RtpemStats stats_rtp;
1189 var OsmuxemStats stats_osmux;
1190 var MgcpResponse resp;
1191 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1192 var MgcpCallId call_id := '1226'H;
1193 var integer tolerance := 0;
1194
1195 f_init(ep, true);
1196
1197 /* from us to MGW */
1198 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1199 flow[0].rtp_cfg := c_RtpemDefaultCfg
1200 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1201 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1202 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);
1203 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1204 /* bind local RTP emulation sockets */
1205 flow[0].em.portnr := 10000;
1206 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1207
1208 /* from MGW back to us */
1209 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "AMR/8000"));
1210 flow[1].em.portnr := mp_local_osmux_port;
1211 flow[1].osmux_cid := 2;
1212 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1213 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1214
1215 if (bidir) {
1216 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1217 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1218
1219 /* Note: When we test bidirectional we may
1220 * loose packets during switch off because
1221 * both ends are transmitting and we only
1222 * can switch them off one by one. */
1223 tolerance := 3;
1224 } else {
1225 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1226 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1227 }
1228
1229 f_sleep(1.0);
1230
1231 /* Switch off both Tx, wait to receive delayed frames from MGW */
1232 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1233 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1234 f_sleep(0.1);
1235
1236 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1237 f_flow_delete(RTPEM[1]);
1238
1239 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1240 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1241 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1242 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1243 mtc.stop;
1244 }
1245
1246 f_rtpem_stats_err_check(stats_rtp);
1247 f_osmuxem_stats_err_check(stats_osmux);
1248
1249 setverdict(pass);
1250 }
1251
1252 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1253 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
1254 f_TC_two_crcx_and_rtp_osmux(false);
1255 }
1256
1257 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1258 * exchange some data in both directions */
1259 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
1260 f_TC_two_crcx_and_rtp_osmux(true);
1261 }
1262
1263
1264 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard) runs on dummy_CT {
1265 var RtpFlowData flow[2];
1266 var RtpemStats stats_rtp;
1267 var OsmuxemStats stats_osmux;
1268 var MgcpResponse resp;
1269 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1270 var MgcpCallId call_id := '1227'H;
1271 var integer num_pkts_tx[2];
1272 var integer temp;
1273
1274 f_init(ep, true);
1275
1276 /* Create the first connection in receive only mode */
1277 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1278 flow[0].rtp_cfg := c_RtpemDefaultCfg
1279 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1280 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1281 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);
1282 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1283 /* bind local RTP emulation sockets */
1284 flow[0].em.portnr := 10000;
1285 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1286
1287
1288 /* Create the second connection. This connection will be also
1289 * in receive only mode */
1290 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "AMR/8000"));
1291 flow[1].em.portnr := mp_local_osmux_port;
1292 if (crcx_osmux_wildcard) {
1293 flow[1].osmux_cid := -1;
1294 } else {
1295 flow[1].osmux_cid := 2;
1296 }
1297 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1298 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], false);
1299
1300
1301 /* The first leg starts transmitting */
1302 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1303 f_sleep(0.5);
1304 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1305 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1306 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1307 mtc.stop;
1308 }
1309 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1310 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1311 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1312 mtc.stop;
1313 }
1314
1315 /* The second leg starts transmitting a little later */
1316 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1317 f_sleep(1.0);
1318 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1319 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1320 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1321 mtc.stop;
1322 }
1323 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1324 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1325 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1326 mtc.stop;
1327 }
1328
1329 /* The first leg will now be switched into bidirectional
1330 * mode, but we do not expect any data comming back yet. */
1331 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1332 f_sleep(0.5);
1333 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1334 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1335 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1336 mtc.stop;
1337 }
1338 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1339 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1340 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1341 mtc.stop;
1342 }
1343
1344 /* When the second leg is switched into bidirectional mode
1345 * as well, then the MGW will connect the two together and
1346 * we should see RTP streams passing through from both ends. */
1347 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1348 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1349 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1350 num_pkts_tx[0] := stats_rtp.num_pkts_tx
1351 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1352 num_pkts_tx[1] := stats_osmux.num_pkts_tx
1353
1354 if (crcx_osmux_wildcard) {
1355 /* For now we must set same CID as the MGW recvCID,
1356 * having sendCID!=recvCID is not yet supported. */
1357 flow[1].osmux_cid := flow[1].osmux_cid_response;
1358 }
1359 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1360 f_sleep(2.0);
1361
1362 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1363 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1364
1365 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1366 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1367 log("stats_rtp: ", stats_rtp);
1368 log("stats_osmux: ", stats_osmux);
1369 log("old_rtp_tx: ", num_pkts_tx[0]);
1370 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1371 mtc.stop;
1372 }
1373
1374 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1375 if (temp > 3 or temp < -3) {
1376 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1377 mtc.stop;
1378 }
1379
1380 f_rtpem_stats_err_check(stats_rtp);
1381 f_osmuxem_stats_err_check(stats_osmux);
1382
1383 /* Tear down */
1384 f_flow_delete(RTPEM[0]);
1385 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1386 setverdict(pass);
1387 }
1388
1389 /* create one RTP and one OSmux emulations and pass data in both
1390 directions. Create CRCX with wildcard Osmux CID and set it later
1391 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1392 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
1393 f_two_crcx_mdcx_and_rtp_osmux(true);
1394 }
1395
1396 /* create one RTP and one OSmux emulations and pass data in both
1397 directions. Create CRCX with fixed Osmux CID and keep it during
1398 MDCX. This is similar to how BSC sets up the call in AoIP. */
1399 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
1400 f_two_crcx_mdcx_and_rtp_osmux(false);
1401 }
1402
Harald Welte646ecdb2017-12-28 03:21:57 +01001403 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1404 var template MgcpCommand cmd;
1405 var MgcpResponse resp;
1406
1407 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1408 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1409
1410 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1411
1412 setverdict(pass);
1413 }
1414
1415 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1416 var MgcpEndpoint ep;
1417 var MgcpCallId call_id;
1418 var integer ep_nr;
1419
1420 f_init();
1421
1422 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001423 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
Harald Welte646ecdb2017-12-28 03:21:57 +01001424 call_id := int2hex(ep_nr, 2) & '1234'H;
1425 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1426 }
1427 }
1428
Harald Welte79181ff2017-11-18 19:26:11 +01001429 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1430 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001431 var template MgcpCommand cmd;
1432 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001433 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001434 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001435
Harald Welteedc45c12017-11-18 19:15:05 +01001436 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001437
Harald Welteba62c8c2017-11-18 18:26:49 +01001438 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001439 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001440
Harald Welteba62c8c2017-11-18 18:26:49 +01001441 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001442
1443 setverdict(pass);
1444 }
1445
Harald Welte79181ff2017-11-18 19:26:11 +01001446 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1447 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1448 var template MgcpCommand cmd;
1449 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001450 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001451 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001452
1453 f_init(ep);
1454
1455 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1456 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1457
1458 f_dlcx_ok(ep);
1459
1460 setverdict(pass);
1461 }
1462
1463
Harald Welte6d167f82017-11-18 19:41:35 +01001464 /* CRCX + DLCX of valid endpoint but invalid call-id */
1465 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1466 var template MgcpCommand cmd;
1467 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001468 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001469 var MgcpCallId call_id := '51231'H;
1470
1471 f_init(ep);
1472
1473 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1474 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1475
1476 f_dlcx(ep, "516", *, 'ffff'H);
1477
1478 setverdict(pass);
1479 }
1480
1481
1482 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1483 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1484 var template MgcpCommand cmd;
1485 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001486 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001487 var MgcpCallId call_id := '51230'H;
1488
1489 f_init(ep);
1490
1491 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1492 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1493
1494 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1495
1496 setverdict(pass);
1497 }
1498
1499
Harald Weltee636afd2017-09-17 16:24:09 +08001500 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001501 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1502 var template MgcpCommand cmd;
1503 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001504 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001505 var MgcpCallId call_id := '51229'H;
1506 var template MgcpResponse rtmpl := {
1507 line := {
1508 code := "200",
1509 string := "OK"
1510 },
1511 params:= { },
1512 sdp := omit
1513 };
1514
1515 f_init(ep);
1516
1517 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1518 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1519
1520 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1521 resp := mgcp_transceive_mgw(cmd, rtmpl);
1522 resp := mgcp_transceive_mgw(cmd, rtmpl);
1523
1524 setverdict(pass);
1525 }
1526
Harald Weltebb7523b2018-03-29 08:52:01 +02001527 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1528 charstring codec) := {
1529 em := {
1530 hostname := host_a,
1531 portnr := omit
1532 },
1533 mgw := {
1534 hostname := host_b,
1535 portnr := omit
1536 },
1537 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001538 codec := codec,
1539 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001540 }
Harald Weltef53f1642017-11-18 19:57:11 +01001541
Harald Weltebb7523b2018-03-29 08:52:01 +02001542 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1543 testcase TC_rtpem_selftest() runs on dummy_CT {
1544 var RtpemStats stats[2];
1545 var integer local_port := 10000;
1546 var integer local_port2 := 20000;
1547
1548 f_init();
1549
1550 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1551 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1552
1553 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1554 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1555
1556 log("=== starting");
1557 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1558 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1559
1560 f_sleep(5.0);
1561
1562 log("=== stopping");
1563 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1564 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1565 f_sleep(0.5);
1566 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1567 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1568
1569 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1570 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1571 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1572 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001573 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001574 }
1575 setverdict(pass);
1576 }
1577
Philipp Maier2321ef92018-06-27 17:52:04 +02001578 /* Create one half open connection in receive-only mode. The MGW must accept
1579 * the packets but must not send any. */
1580 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1581 var RtpFlowData flow;
1582 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1583 var MgcpCallId call_id := '1225'H;
1584 var RtpemStats stats;
1585
1586 f_init(ep);
1587 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
1588 flow.em.portnr := 10000;
1589 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, false);
1590
1591 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1592 f_sleep(1.0);
1593 f_flow_delete(RTPEM[0], ep, call_id);
1594
1595 stats := f_rtpem_stats_get(RTPEM[0]);
1596
Philipp Maier42b17cc2019-10-01 13:53:17 +02001597 /* Make sure that at least some amount of RTP packets/bytes
1598 * have has been transmitted. The compare values for
1599 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1600 * using a testrun and the results were devided by 2, so even
1601 * in load situations we should reach the minimum amount of
1602 * required packets/bytes */
1603
1604 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001605 setverdict(fail);
1606 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001607 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001608 setverdict(fail);
1609 }
Philipp Maier36291392018-07-25 09:40:44 +02001610
1611 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001612
1613 setverdict(pass);
1614 }
1615
1616 /* Create one connection in loopback mode, test if the RTP packets are
1617 * actually reflected */
1618 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
1619 var RtpFlowData flow;
1620 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1621 var MgcpCallId call_id := '1225'H;
1622 var RtpemStats stats;
1623
1624 f_init(ep);
1625 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1626 flow.em.portnr := 10000;
1627 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
1628
1629 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1630 f_sleep(1.0);
1631 f_flow_delete(RTPEM[0], ep, call_id);
1632
1633 stats := f_rtpem_stats_get(RTPEM[0]);
1634
1635 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1636 setverdict(fail);
1637 }
1638 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1639 setverdict(fail);
1640 }
Philipp Maier36291392018-07-25 09:40:44 +02001641
1642 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001643
1644 setverdict(pass);
1645 }
1646
Philipp Maier7df85f62018-07-25 10:26:09 +02001647 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1648 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001649 var RtpFlowData flow[2];
1650 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001651 var MgcpResponse resp;
1652 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1653 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001654 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001655
1656 f_init(ep);
1657
1658 /* from us to MGW */
Philipp Maier7df85f62018-07-25 10:26:09 +02001659 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001660 /* bind local RTP emulation sockets */
1661 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001662 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001663
1664 /* from MGW back to us */
Philipp Maier7df85f62018-07-25 10:26:09 +02001665 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001666 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001667 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1668
1669 if (bidir) {
1670 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1671 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1672
1673 /* Note: When we test bidirectional we may
1674 * loose packets during switch off because
1675 * both ends are transmitting and we only
1676 * can switch them off one by one. */
1677 tolerance := 3;
1678 } else {
1679 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1680 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1681 }
1682
1683 f_sleep(1.0);
1684
1685 f_flow_delete(RTPEM[1]);
1686 f_flow_delete(RTPEM[0], ep, call_id);
1687
1688 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1689 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1690 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1691 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001692 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001693 }
1694
Philipp Maier36291392018-07-25 09:40:44 +02001695 f_rtpem_stats_err_check(stats[0]);
1696 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001697
Philipp Maier2321ef92018-06-27 17:52:04 +02001698 setverdict(pass);
1699 }
1700
1701 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1702 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001703 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001704 }
1705
1706 /* create two local RTP emulations; create two connections on MGW EP,
1707 * exchange some data in both directions */
1708 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001709 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1710 }
1711
1712 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1713 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1714 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1715 }
1716
1717 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1718 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1719 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001720 }
1721
1722 /* create two local RTP emulations and pass data in both directions */
1723 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1724 var RtpFlowData flow[2];
1725 var RtpemStats stats[2];
1726 var MgcpResponse resp;
1727 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1728 var MgcpCallId call_id := '1227'H;
1729 var integer num_pkts_tx[2];
1730 var integer temp;
1731
1732 f_init(ep);
1733
1734 /* Create the first connection in receive only mode */
1735 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1736 flow[0].em.portnr := 10000;
1737 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1738
1739 /* Create the second connection. This connection will be also
1740 * in receive only mode */
1741 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1742 flow[1].em.portnr := 20000;
1743 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], false);
1744
1745 /* The first leg starts transmitting */
1746 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1747 f_sleep(0.5);
1748 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1749 if (stats[0].num_pkts_rx_err_disabled != 0) {
1750 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001751 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001752 }
1753 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1754 if (stats[1].num_pkts_rx_err_disabled != 0) {
1755 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001756 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001757 }
1758
1759 /* The second leg starts transmitting a little later */
1760 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1761 f_sleep(1.0);
1762 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1763 if (stats[0].num_pkts_rx_err_disabled != 0) {
1764 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001765 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001766 }
1767 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1768 if (stats[1].num_pkts_rx_err_disabled != 0) {
1769 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001770 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001771 }
1772
1773 /* The first leg will now be switched into bidirectional
1774 * mode, but we do not expect any data comming back yet. */
1775 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1776 f_sleep(0.5);
1777 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1778 if (stats[1].num_pkts_rx_err_disabled != 0) {
1779 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001780 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001781 }
1782 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1783 if (stats[1].num_pkts_rx_err_disabled != 0) {
1784 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001785 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001786 }
1787
1788 /* When the second leg is switched into bidirectional mode
1789 * as well, then the MGW will connect the two together and
1790 * we should see RTP streams passing through from both ends. */
1791 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1792 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1793 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1794 num_pkts_tx[0] := stats[0].num_pkts_tx
1795 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1796 num_pkts_tx[1] := stats[1].num_pkts_tx
1797
1798 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1799 f_sleep(2.0);
1800
1801 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1802 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1803
1804 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1805 if (temp > 3 or temp < -3) {
1806 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001807 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001808 }
1809
1810 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1811 if (temp > 3 or temp < -3) {
1812 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001813 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001814 }
Philipp Maier36291392018-07-25 09:40:44 +02001815
1816 f_rtpem_stats_err_check(stats[0]);
1817 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001818
1819 /* Tear down */
1820 f_flow_delete(RTPEM[0]);
1821 f_flow_delete(RTPEM[1], ep, call_id);
1822 setverdict(pass);
1823 }
1824
1825 /* Test what happens when two RTP streams from different sources target
1826 * a single connection. Is the unsolicited stream properly ignored? */
1827 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1828 var RtpFlowData flow[2];
1829 var RtpemStats stats[2];
1830 var MgcpResponse resp;
1831 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1832 var MgcpCallId call_id := '1234321326'H;
1833 var integer unsolicited_port := 10002;
1834
1835 f_init(ep);
1836
1837 /* from us to MGW */
1838 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1839 /* bind local RTP emulation sockets */
1840 flow[0].em.portnr := 10000;
1841 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1842
1843 /* from MGW back to us */
1844 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1845 flow[1].em.portnr := 20000;
1846 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001847
1848 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1849 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1850
Philipp Maier2321ef92018-06-27 17:52:04 +02001851 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001852
Philipp Maier2321ef92018-06-27 17:52:04 +02001853 /* Start inserting unsolicited RTP packets */
Philipp Maier4de3e372018-06-29 17:15:22 +02001854 f_rtpem_bind(RTPEM[2], mp_local_ip, unsolicited_port);
1855 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001856 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1857
1858 f_sleep(0.5);
1859
Daniel Willmanna069d382018-12-13 13:53:33 +01001860 /* Stop transmitting packets and tear down the flows */
1861 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02001862 f_flow_delete(RTPEM[0]);
1863 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001864
1865 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1866 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1867 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1868 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001869 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001870 }
1871
Philipp Maier36291392018-07-25 09:40:44 +02001872 f_rtpem_stats_err_check(stats[0]);
1873 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02001874
Harald Weltebb7523b2018-03-29 08:52:01 +02001875 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001876 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001877
Philipp Maier2321ef92018-06-27 17:52:04 +02001878 /* Test a handover situation. We first create two connections transmit
1879 * some data bidirectionally. Then we will simulate a handover situation. */
1880 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1881 var RtpFlowData flow[2];
1882 var RtpemStats stats[3];
1883 var MgcpResponse resp;
1884 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1885 var MgcpCallId call_id := '76338'H;
1886 var integer port_old;
1887
1888 f_init(ep);
1889
1890 /* First connection (BTS) */
1891 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1892 /* bind local RTP emulation sockets */
1893 flow[0].em.portnr := 10000;
1894 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1895
1896 /* Second connection (PBX) */
1897 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1898 flow[1].em.portnr := 20000;
1899 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1900
1901 /* Normal rtp flow for one second */
1902 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1903 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1904 f_sleep(1.0);
1905
1906 /* Now switch the flow over to a new port (BTS) */
1907 port_old := flow[0].em.portnr;
1908 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001909 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02001910 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001911 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02001912
1913 /* When handing over a call, the old source may still keep
1914 * transmitting for a while. We simulate this by injecting
1915 * some unsolicited packets on the behalf of the old source,
1916 * (old remote port) */
Philipp Maier4de3e372018-06-29 17:15:22 +02001917 f_rtpem_bind(RTPEM[2], mp_local_ip, port_old);
1918 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001919 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1920 f_sleep(1.0);
1921 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
1922 f_sleep(1.0);
1923
1924 /* Terminate call */
1925 f_flow_delete(RTPEM[0]);
1926 f_flow_delete(RTPEM[1], ep, call_id);
1927
1928 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1929 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1930 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
1931 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001932 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001933 }
1934 stats[2] := f_rtpem_stats_get(RTPEM[2]);
1935 if (stats[2].num_pkts_rx_err_disabled != 0) {
1936 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02001937 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001938 }
1939
Philipp Maier36291392018-07-25 09:40:44 +02001940 f_rtpem_stats_err_check(stats[0]);
1941 f_rtpem_stats_err_check(stats[1]);
1942 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02001943
Philipp Maier2321ef92018-06-27 17:52:04 +02001944 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02001945 }
Harald Weltef53f1642017-11-18 19:57:11 +01001946
Philipp Maier6d4e0942019-02-21 17:35:01 +01001947
1948 /* create two local RTP emulations; create two connections on MGW EP, see if
1949 * exchanged data is converted bwtween ts101318 and rfc5993 */
1950 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
1951 var RtpFlowData flow[2];
1952 var RtpemStats stats[2];
1953 var MgcpResponse resp;
1954 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1955 var MgcpCallId call_id := '1226'H;
1956
1957 f_init(ep);
1958
1959 /* Turn on conversion mode */
1960 f_vty_enter_config(MGWVTY);
1961 f_vty_transceive(MGWVTY, "mgcp");
1962 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
1963
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02001964 /* Connection #0 (Bidirectional) */
Philipp Maier6d4e0942019-02-21 17:35:01 +01001965 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1966 /* bind local RTP emulation sockets */
1967 flow[0].em.portnr := 10000;
1968 flow[0].rtp_cfg := c_RtpemDefaultCfg;
1969 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1970 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1971 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1972 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1973
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02001974 /* Connection #1 (Bidirectional) */
Philipp Maier6d4e0942019-02-21 17:35:01 +01001975 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1976 flow[1].em.portnr := 20000;
1977 flow[1].rtp_cfg := c_RtpemDefaultCfg;
1978 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
1979 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1980 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1981 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1982
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02001983 /* Send RTP packets to connection #0, receive on connection #1 */
1984 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1985 f_sleep(0.5);
1986 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01001987 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02001988 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1989 f_sleep(0.5);
1990 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01001991
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02001992 /* Send RTP packets to connection #1, receive on connection #0 */
1993 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1994 f_sleep(0.5);
1995 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1996 f_sleep(1.0);
1997 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1998 f_sleep(0.5);
1999 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2000
2001 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002002 f_flow_delete(RTPEM[0]);
2003 f_flow_delete(RTPEM[1], ep, call_id);
2004
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002005 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002006 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2007 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002008 f_rtpem_stats_err_check(stats[0]);
2009 f_rtpem_stats_err_check(stats[1]);
2010
2011 /* Turn off conversion mode */
2012 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2013
2014 setverdict(pass);
2015 }
2016
Philipp Maier4f764ce2019-03-07 10:54:10 +01002017 /* create two local RTP emulations; create two connections on MGW EP, see if
2018 * exchanged data is converted between AMR octet-aligned and bandwith
2019 * efficient-mode */
2020 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2021 var RtpFlowData flow[2];
2022 var RtpemStats stats[2];
2023 var MgcpResponse resp;
2024 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2025 var MgcpCallId call_id := '1226'H;
2026
2027 f_init(ep);
2028
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002029 /* Connection #0 (Bidirectional) */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002030 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
2031 /* bind local RTP emulation sockets */
2032 flow[0].em.portnr := 10000;
2033 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2034 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2035 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2036 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2037 flow[0].fmtp := fmtp0;
2038 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2039
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002040 /* Connection #1 (Bidirectional) */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002041 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
2042 flow[1].em.portnr := 20000;
2043 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2044 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2045 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2046 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2047 flow[1].fmtp := fmtp1;
2048 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2049
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002050 /* Send RTP packets to connection #0, receive on connection #1 */
2051 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2052 f_sleep(0.5);
2053 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002054 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002055 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2056 f_sleep(0.5);
2057 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002058
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002059 /* Send RTP packets to connection #1, receive on connection #0 */
2060 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2061 f_sleep(0.5);
2062 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2063 f_sleep(1.0);
2064 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2065 f_sleep(0.5);
2066 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2067
2068 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002069 f_flow_delete(RTPEM[0]);
2070 f_flow_delete(RTPEM[1], ep, call_id);
2071
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002072 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002073 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2074 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002075 f_rtpem_stats_err_check(stats[0]);
2076 f_rtpem_stats_err_check(stats[1]);
2077
2078 setverdict(pass);
2079 }
2080
2081 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
2082 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=1", "octet-align=0");
2083 }
2084
2085 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2086 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2087 }
2088
2089 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2090 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2091 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002092
Harald Weltee636afd2017-09-17 16:24:09 +08002093 /* TODO: Double-DLCX (no retransmission) */
2094
2095
2096
2097 /* TODO: AUEP (various) */
2098 /* TODO: RSIP (various) */
2099 /* TODO: RQNT (various) */
2100 /* TODO: EPCF (various) */
2101 /* TODO: AUCX (various) */
2102 /* TODO: invalid verb (various) */
2103
Oliver Smith021141e2019-06-25 12:09:01 +02002104
2105 testcase TC_conn_timeout() runs on dummy_CT {
2106 var RtpFlowData flow;
2107 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2108 var MgcpCallId call_id := '1225'H;
2109 var MGCP_RecvFrom mrf;
2110
2111 f_init(ep);
2112 log("Setting conn-timeout to 1s");
2113 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2114
2115 log("Sending RTP data for 1.5s");
2116 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
2117 flow.em.portnr := 10000;
2118 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2119 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2120 f_sleep(1.5);
2121
2122 log("Stopping for 0.5s and resuming");
2123 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2124 f_sleep(0.5);
2125 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2126 f_sleep(0.1);
2127
2128 log("Stopping for 1.5s, expecting to run into timeout");
2129 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2130 f_sleep(1.5);
2131
2132 log("Resuming should fail now");
2133 f_rtpem_conn_refuse_expect(RTPEM[0]);
2134 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2135 f_sleep(0.2);
2136 f_rtpem_conn_refuse_verify(RTPEM[0]);
2137
2138 setverdict(pass);
2139 }
2140
Harald Welte00a067f2017-09-13 23:27:17 +02002141 control {
2142 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002143 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002144 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002145 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002146 execute(TC_crcx_unsupp_mode());
2147 execute(TC_crcx_early_bidir_mode());
2148 execute(TC_crcx_unsupp_param());
2149 execute(TC_crcx_missing_callid());
2150 execute(TC_crcx_missing_mode());
2151 execute(TC_crcx_unsupp_packet_intv());
2152 execute(TC_crcx_illegal_double_lco());
2153 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002154 execute(TC_crcx_wildcarded());
2155 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002156 execute(TC_mdcx_without_crcx());
2157 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002158 execute(TC_mdcx_wildcarded());
2159 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002160 execute(TC_crcx_and_dlcx_ep_callid_connid());
2161 execute(TC_crcx_and_dlcx_ep_callid());
2162 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002163 execute(TC_crcx_and_dlcx_ep_callid_inval());
2164 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002165 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002166
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002167 execute(TC_crcx_osmux_wildcard());
2168 execute(TC_crcx_osmux_fixed());
2169 execute(TC_crcx_osmux_fixed_twice());
2170 execute(TC_one_crcx_receive_only_osmux());
2171 execute(TC_one_crcx_loopback_osmux());
2172 execute(TC_two_crcx_and_rtp_osmux());
2173 execute(TC_two_crcx_and_rtp_osmux_bidir());
2174 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2175 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2176
Harald Welte33d82162017-12-28 03:21:57 +01002177 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002178
2179 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002180
2181 execute(TC_one_crcx_receive_only_rtp());
2182 execute(TC_one_crcx_loopback_rtp());
Harald Weltebb7523b2018-03-29 08:52:01 +02002183 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002184 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002185 execute(TC_two_crcx_diff_pt_and_rtp());
2186 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002187 execute(TC_two_crcx_mdcx_and_rtp());
2188 execute(TC_two_crcx_and_unsolicited_rtp());
2189 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002190 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002191 execute(TC_amr_oa_bwe_rtp_conversion());
2192 execute(TC_amr_oa_oa_rtp_conversion());
2193 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002194
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002195 execute(TC_conn_timeout());
Harald Welte00a067f2017-09-13 23:27:17 +02002196 }
2197}