blob: 31c2a1db34c9f0f148a28491024a9567cd8c6545 [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
1597 if (stats.num_pkts_tx < 40) {
1598 setverdict(fail);
1599 }
1600 if (stats.bytes_payload_tx < 190) {
1601 setverdict(fail);
1602 }
Philipp Maier36291392018-07-25 09:40:44 +02001603
1604 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001605
1606 setverdict(pass);
1607 }
1608
1609 /* Create one connection in loopback mode, test if the RTP packets are
1610 * actually reflected */
1611 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
1612 var RtpFlowData flow;
1613 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1614 var MgcpCallId call_id := '1225'H;
1615 var RtpemStats stats;
1616
1617 f_init(ep);
1618 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1619 flow.em.portnr := 10000;
1620 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
1621
1622 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1623 f_sleep(1.0);
1624 f_flow_delete(RTPEM[0], ep, call_id);
1625
1626 stats := f_rtpem_stats_get(RTPEM[0]);
1627
1628 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1629 setverdict(fail);
1630 }
1631 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1632 setverdict(fail);
1633 }
Philipp Maier36291392018-07-25 09:40:44 +02001634
1635 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001636
1637 setverdict(pass);
1638 }
1639
Philipp Maier7df85f62018-07-25 10:26:09 +02001640 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1641 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001642 var RtpFlowData flow[2];
1643 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001644 var MgcpResponse resp;
1645 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1646 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001647 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001648
1649 f_init(ep);
1650
1651 /* from us to MGW */
Philipp Maier7df85f62018-07-25 10:26:09 +02001652 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001653 /* bind local RTP emulation sockets */
1654 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001655 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001656
1657 /* from MGW back to us */
Philipp Maier7df85f62018-07-25 10:26:09 +02001658 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001659 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001660 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1661
1662 if (bidir) {
1663 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1664 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1665
1666 /* Note: When we test bidirectional we may
1667 * loose packets during switch off because
1668 * both ends are transmitting and we only
1669 * can switch them off one by one. */
1670 tolerance := 3;
1671 } else {
1672 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1673 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1674 }
1675
1676 f_sleep(1.0);
1677
1678 f_flow_delete(RTPEM[1]);
1679 f_flow_delete(RTPEM[0], ep, call_id);
1680
1681 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1682 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1683 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1684 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001685 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001686 }
1687
Philipp Maier36291392018-07-25 09:40:44 +02001688 f_rtpem_stats_err_check(stats[0]);
1689 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001690
Philipp Maier2321ef92018-06-27 17:52:04 +02001691 setverdict(pass);
1692 }
1693
1694 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1695 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001696 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001697 }
1698
1699 /* create two local RTP emulations; create two connections on MGW EP,
1700 * exchange some data in both directions */
1701 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001702 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1703 }
1704
1705 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1706 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1707 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1708 }
1709
1710 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1711 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1712 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001713 }
1714
1715 /* create two local RTP emulations and pass data in both directions */
1716 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1717 var RtpFlowData flow[2];
1718 var RtpemStats stats[2];
1719 var MgcpResponse resp;
1720 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1721 var MgcpCallId call_id := '1227'H;
1722 var integer num_pkts_tx[2];
1723 var integer temp;
1724
1725 f_init(ep);
1726
1727 /* Create the first connection in receive only mode */
1728 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1729 flow[0].em.portnr := 10000;
1730 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1731
1732 /* Create the second connection. This connection will be also
1733 * in receive only mode */
1734 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1735 flow[1].em.portnr := 20000;
1736 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], false);
1737
1738 /* The first leg starts transmitting */
1739 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1740 f_sleep(0.5);
1741 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1742 if (stats[0].num_pkts_rx_err_disabled != 0) {
1743 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001744 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001745 }
1746 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1747 if (stats[1].num_pkts_rx_err_disabled != 0) {
1748 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001749 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001750 }
1751
1752 /* The second leg starts transmitting a little later */
1753 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1754 f_sleep(1.0);
1755 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1756 if (stats[0].num_pkts_rx_err_disabled != 0) {
1757 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001758 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001759 }
1760 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1761 if (stats[1].num_pkts_rx_err_disabled != 0) {
1762 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001763 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001764 }
1765
1766 /* The first leg will now be switched into bidirectional
1767 * mode, but we do not expect any data comming back yet. */
1768 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1769 f_sleep(0.5);
1770 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1771 if (stats[1].num_pkts_rx_err_disabled != 0) {
1772 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001773 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001774 }
1775 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1776 if (stats[1].num_pkts_rx_err_disabled != 0) {
1777 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001778 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001779 }
1780
1781 /* When the second leg is switched into bidirectional mode
1782 * as well, then the MGW will connect the two together and
1783 * we should see RTP streams passing through from both ends. */
1784 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1785 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1786 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1787 num_pkts_tx[0] := stats[0].num_pkts_tx
1788 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1789 num_pkts_tx[1] := stats[1].num_pkts_tx
1790
1791 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1792 f_sleep(2.0);
1793
1794 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1795 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1796
1797 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1798 if (temp > 3 or temp < -3) {
1799 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001800 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001801 }
1802
1803 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1804 if (temp > 3 or temp < -3) {
1805 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001806 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001807 }
Philipp Maier36291392018-07-25 09:40:44 +02001808
1809 f_rtpem_stats_err_check(stats[0]);
1810 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001811
1812 /* Tear down */
1813 f_flow_delete(RTPEM[0]);
1814 f_flow_delete(RTPEM[1], ep, call_id);
1815 setverdict(pass);
1816 }
1817
1818 /* Test what happens when two RTP streams from different sources target
1819 * a single connection. Is the unsolicited stream properly ignored? */
1820 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1821 var RtpFlowData flow[2];
1822 var RtpemStats stats[2];
1823 var MgcpResponse resp;
1824 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1825 var MgcpCallId call_id := '1234321326'H;
1826 var integer unsolicited_port := 10002;
1827
1828 f_init(ep);
1829
1830 /* from us to MGW */
1831 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1832 /* bind local RTP emulation sockets */
1833 flow[0].em.portnr := 10000;
1834 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1835
1836 /* from MGW back to us */
1837 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1838 flow[1].em.portnr := 20000;
1839 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001840
1841 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1842 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1843
Philipp Maier2321ef92018-06-27 17:52:04 +02001844 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001845
Philipp Maier2321ef92018-06-27 17:52:04 +02001846 /* Start inserting unsolicited RTP packets */
Philipp Maier4de3e372018-06-29 17:15:22 +02001847 f_rtpem_bind(RTPEM[2], mp_local_ip, unsolicited_port);
1848 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001849 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1850
1851 f_sleep(0.5);
1852
Daniel Willmanna069d382018-12-13 13:53:33 +01001853 /* Stop transmitting packets and tear down the flows */
1854 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02001855 f_flow_delete(RTPEM[0]);
1856 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001857
1858 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1859 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1860 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1861 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001862 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001863 }
1864
Philipp Maier36291392018-07-25 09:40:44 +02001865 f_rtpem_stats_err_check(stats[0]);
1866 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02001867
Harald Weltebb7523b2018-03-29 08:52:01 +02001868 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001869 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001870
Philipp Maier2321ef92018-06-27 17:52:04 +02001871 /* Test a handover situation. We first create two connections transmit
1872 * some data bidirectionally. Then we will simulate a handover situation. */
1873 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1874 var RtpFlowData flow[2];
1875 var RtpemStats stats[3];
1876 var MgcpResponse resp;
1877 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1878 var MgcpCallId call_id := '76338'H;
1879 var integer port_old;
1880
1881 f_init(ep);
1882
1883 /* First connection (BTS) */
1884 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1885 /* bind local RTP emulation sockets */
1886 flow[0].em.portnr := 10000;
1887 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1888
1889 /* Second connection (PBX) */
1890 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1891 flow[1].em.portnr := 20000;
1892 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1893
1894 /* Normal rtp flow for one second */
1895 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1896 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1897 f_sleep(1.0);
1898
1899 /* Now switch the flow over to a new port (BTS) */
1900 port_old := flow[0].em.portnr;
1901 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001902 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02001903 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001904 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02001905
1906 /* When handing over a call, the old source may still keep
1907 * transmitting for a while. We simulate this by injecting
1908 * some unsolicited packets on the behalf of the old source,
1909 * (old remote port) */
Philipp Maier4de3e372018-06-29 17:15:22 +02001910 f_rtpem_bind(RTPEM[2], mp_local_ip, port_old);
1911 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001912 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1913 f_sleep(1.0);
1914 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
1915 f_sleep(1.0);
1916
1917 /* Terminate call */
1918 f_flow_delete(RTPEM[0]);
1919 f_flow_delete(RTPEM[1], ep, call_id);
1920
1921 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1922 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1923 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
1924 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001925 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001926 }
1927 stats[2] := f_rtpem_stats_get(RTPEM[2]);
1928 if (stats[2].num_pkts_rx_err_disabled != 0) {
1929 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02001930 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001931 }
1932
Philipp Maier36291392018-07-25 09:40:44 +02001933 f_rtpem_stats_err_check(stats[0]);
1934 f_rtpem_stats_err_check(stats[1]);
1935 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02001936
Philipp Maier2321ef92018-06-27 17:52:04 +02001937 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02001938 }
Harald Weltef53f1642017-11-18 19:57:11 +01001939
Philipp Maier6d4e0942019-02-21 17:35:01 +01001940
1941 /* create two local RTP emulations; create two connections on MGW EP, see if
1942 * exchanged data is converted bwtween ts101318 and rfc5993 */
1943 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
1944 var RtpFlowData flow[2];
1945 var RtpemStats stats[2];
1946 var MgcpResponse resp;
1947 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1948 var MgcpCallId call_id := '1226'H;
1949
1950 f_init(ep);
1951
1952 /* Turn on conversion mode */
1953 f_vty_enter_config(MGWVTY);
1954 f_vty_transceive(MGWVTY, "mgcp");
1955 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
1956
1957 /* from us to MGW */
1958 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1959 /* bind local RTP emulation sockets */
1960 flow[0].em.portnr := 10000;
1961 flow[0].rtp_cfg := c_RtpemDefaultCfg;
1962 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1963 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1964 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1965 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1966
1967 /* from MGW back to us */
1968 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1969 flow[1].em.portnr := 20000;
1970 flow[1].rtp_cfg := c_RtpemDefaultCfg;
1971 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
1972 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1973 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1974 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1975
1976 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1977 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1978
1979 f_sleep(1.0);
1980
1981 f_flow_delete(RTPEM[0]);
1982 f_flow_delete(RTPEM[1], ep, call_id);
1983
1984 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1985 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1986
1987 f_rtpem_stats_err_check(stats[0]);
1988 f_rtpem_stats_err_check(stats[1]);
1989
1990 /* Turn off conversion mode */
1991 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
1992
1993 setverdict(pass);
1994 }
1995
Philipp Maier4f764ce2019-03-07 10:54:10 +01001996 /* create two local RTP emulations; create two connections on MGW EP, see if
1997 * exchanged data is converted between AMR octet-aligned and bandwith
1998 * efficient-mode */
1999 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2000 var RtpFlowData flow[2];
2001 var RtpemStats stats[2];
2002 var MgcpResponse resp;
2003 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2004 var MgcpCallId call_id := '1226'H;
2005
2006 f_init(ep);
2007
2008 /* from us to MGW */
2009 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
2010 /* bind local RTP emulation sockets */
2011 flow[0].em.portnr := 10000;
2012 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2013 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2014 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2015 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2016 flow[0].fmtp := fmtp0;
2017 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2018
2019 /* from MGW back to us */
2020 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
2021 flow[1].em.portnr := 20000;
2022 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2023 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2024 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2025 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2026 flow[1].fmtp := fmtp1;
2027 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2028
2029 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2030 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2031
2032 f_sleep(1.0);
2033
2034 f_flow_delete(RTPEM[0]);
2035 f_flow_delete(RTPEM[1], ep, call_id);
2036
2037 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2038 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2039
2040 f_rtpem_stats_err_check(stats[0]);
2041 f_rtpem_stats_err_check(stats[1]);
2042
2043 setverdict(pass);
2044 }
2045
2046 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
2047 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=1", "octet-align=0");
2048 }
2049
2050 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2051 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2052 }
2053
2054 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2055 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2056 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002057
Harald Weltee636afd2017-09-17 16:24:09 +08002058 /* TODO: Double-DLCX (no retransmission) */
2059
2060
2061
2062 /* TODO: AUEP (various) */
2063 /* TODO: RSIP (various) */
2064 /* TODO: RQNT (various) */
2065 /* TODO: EPCF (various) */
2066 /* TODO: AUCX (various) */
2067 /* TODO: invalid verb (various) */
2068
Harald Welte00a067f2017-09-13 23:27:17 +02002069 control {
2070 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002071 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002072 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002073 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002074 execute(TC_crcx_unsupp_mode());
2075 execute(TC_crcx_early_bidir_mode());
2076 execute(TC_crcx_unsupp_param());
2077 execute(TC_crcx_missing_callid());
2078 execute(TC_crcx_missing_mode());
2079 execute(TC_crcx_unsupp_packet_intv());
2080 execute(TC_crcx_illegal_double_lco());
2081 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002082 execute(TC_crcx_wildcarded());
2083 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002084 execute(TC_mdcx_without_crcx());
2085 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002086 execute(TC_mdcx_wildcarded());
2087 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002088 execute(TC_crcx_and_dlcx_ep_callid_connid());
2089 execute(TC_crcx_and_dlcx_ep_callid());
2090 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002091 execute(TC_crcx_and_dlcx_ep_callid_inval());
2092 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002093 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002094
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002095 execute(TC_crcx_osmux_wildcard());
2096 execute(TC_crcx_osmux_fixed());
2097 execute(TC_crcx_osmux_fixed_twice());
2098 execute(TC_one_crcx_receive_only_osmux());
2099 execute(TC_one_crcx_loopback_osmux());
2100 execute(TC_two_crcx_and_rtp_osmux());
2101 execute(TC_two_crcx_and_rtp_osmux_bidir());
2102 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2103 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2104
Harald Welte33d82162017-12-28 03:21:57 +01002105 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002106
2107 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002108
2109 execute(TC_one_crcx_receive_only_rtp());
2110 execute(TC_one_crcx_loopback_rtp());
Harald Weltebb7523b2018-03-29 08:52:01 +02002111 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002112 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002113 execute(TC_two_crcx_diff_pt_and_rtp());
2114 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002115 execute(TC_two_crcx_mdcx_and_rtp());
2116 execute(TC_two_crcx_and_unsolicited_rtp());
2117 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002118 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002119 execute(TC_amr_oa_bwe_rtp_conversion());
2120 execute(TC_amr_oa_oa_rtp_conversion());
2121 execute(TC_amr_bwe_bwe_rtp_conversion());
Harald Welte00a067f2017-09-13 23:27:17 +02002122 }
2123}