blob: 4be034bf3c61d8b3c7ff547add2810b62f37d9c7 [file] [log] [blame]
Harald Welte34b5a952019-05-27 11:54:11 +02001/* MGW (Media Gateway) test suite in TTCN-3
2 * (C) 2017-2018 Harald Welte <laforge@gnumonks.org>
3 * (C) 2018-2019 sysmocom - s.f.m.c. GmbH
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
Harald Welte00a067f2017-09-13 23:27:17 +020012module MGCP_Test {
Harald Weltef07c2862017-11-18 17:16:24 +010013 import from Osmocom_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +020014 import from MGCP_Types all;
Harald Welte4029e8c2017-11-23 22:00:42 +010015 import from MGCP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080016 import from SDP_Types all;
17 import from MGCP_CodecPort all;
18 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +010019 import from RTP_CodecPort all;
20 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020021 import from RTP_Emulation all;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020022 import from OSMUX_Types all;
23 import from OSMUX_CodecPort all;
24 import from OSMUX_CodecPort_CtrlFunct all;
25 import from OSMUX_Emulation all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080026 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010027 import from General_Types all;
28 import from Native_Functions all;
29 import from IPCP_Types all;
30 import from IP_Types all;
31 import from Osmocom_VTY_Functions all;
32 import from TELNETasp_PortType all;
Harald Welte00a067f2017-09-13 23:27:17 +020033
Philipp Maierbb7a01c2018-02-01 12:32:57 +010034 const charstring c_mgw_domain := "mgw";
35 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
36
Harald Welte21ba5572017-09-19 17:55:05 +080037 /* any variables declared in the component will be available to
38 * all functions that 'run on' the named component, similar to
39 * class members in C++ */
Harald Welte00a067f2017-09-13 23:27:17 +020040 type component dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080041 port MGCP_CODEC_PT MGCP;
42 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010043 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080044 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010045
Philipp Maier2321ef92018-06-27 17:52:04 +020046 var RTP_Emulation_CT vc_RTPEM[3];
47 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010048
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020049 var OSMUX_Emulation_CT vc_OsmuxEM;
50 port OsmuxEM_CTRL_PT OsmuxEM;
51
Philipp Maier6137c322019-02-20 16:13:41 +010052 port TELNETasp_PT MGWVTY;
Harald Welte00a067f2017-09-13 23:27:17 +020053 };
54
Harald Weltee1e18c52017-09-17 16:23:07 +080055 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
56 var MgcpTransId tid := int2str(g_trans_id);
57 g_trans_id := g_trans_id + 1;
58 return tid;
59 }
60
Harald Welte21ba5572017-09-19 17:55:05 +080061 /* all parameters declared here can be modified / overridden by
62 * the config file in the [MODULE_PARAMETERS] section. If no
63 * config file is used or the file doesn't specify them, the
64 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080065 modulepar {
66 PortNumber mp_local_udp_port := 2727;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020067 charstring mp_local_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020068 charstring mp_local_ipv6 := "::1";
Harald Welte3c6ebb92017-09-16 00:56:57 +080069 PortNumber mp_remote_udp_port := 2427;
Pau Espin Pedrolb604af02020-09-07 17:12:39 +020070 charstring mp_remote_ipv4 := "127.0.0.1";
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +020071 charstring mp_remote_ipv6 := "::1";
Harald Weltef07c2862017-11-18 17:16:24 +010072 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020073 PortNumber mp_local_osmux_port := 1985;
Harald Welte3c6ebb92017-09-16 00:56:57 +080074 }
75
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020076 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
77 /* Turn on conversion mode */
78 f_vty_enter_config(MGWVTY);
79 f_vty_transceive(MGWVTY, "mgcp");
80 if (osmux_on) {
81 f_vty_transceive(MGWVTY, "osmux on");
82 } else {
83 f_vty_transceive(MGWVTY, "osmux off");
84 }
85 f_vty_transceive(MGWVTY, "exit");
86 f_vty_transceive(MGWVTY, "exit");
87
88 }
89
90 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +010091 map(self:MGWVTY, system:MGWVTY);
92 f_vty_set_prompts(MGWVTY);
93 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020094
95 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +010096 }
97
Harald Weltebb7523b2018-03-29 08:52:01 +020098 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
99 runs on dummy_CT {
100 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
101 map(comp_ref:RTP, system:RTP);
102 map(comp_ref:RTCP, system:RTCP);
103 comp_ref.start(RTP_Emulation.f_main());
104 }
105
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200106 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
107 runs on dummy_CT {
108 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
109 map(comp_ref:OSMUX, system:OSMUX);
110 comp_ref.start(OSMUX_Emulation.f_main());
111 }
112
Harald Welte21ba5572017-09-19 17:55:05 +0800113 /* initialization function, called by each test case at the
114 * beginning, but 'initialized' variable ensures its body is
115 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200116 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800117 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100118 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200119
Harald Welteedc45c12017-11-18 19:15:05 +0100120 if (initialized == false) {
121 initialized := true;
122
123 /* some random number for the initial transaction id */
124 g_trans_id := float2int(rnd()*65535.0);
125 map(self:MGCP, system:MGCP_CODEC_PT);
126 /* connect the MGCP test port using the given
127 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
128 * */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200129 res := MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, mp_remote_ipv4, mp_remote_udp_port, mp_local_ipv4, mp_local_udp_port, 0, { udp := {} });
Harald Welte9220f632018-05-23 20:27:02 +0200130 if (not ispresent(res.connId)) {
131 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200132 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200133 }
Harald Welteedc45c12017-11-18 19:15:05 +0100134 g_mgcp_conn_id := res.connId;
135
Harald Weltebb7523b2018-03-29 08:52:01 +0200136 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
137 f_rtpem_init(vc_RTPEM[i], i);
138 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
139 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200140 if (osmux_on) {
141 f_osmuxem_init(vc_OsmuxEM);
142 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
143 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800144 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800145
Harald Welteedc45c12017-11-18 19:15:05 +0100146 if (isvalue(ep)) {
147 /* do a DLCX on all connections of the EP */
148 f_dlcx_ignore(valueof(ep));
149 }
Philipp Maier6137c322019-02-20 16:13:41 +0100150
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200151 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800152 }
153
Harald Welte00a067f2017-09-13 23:27:17 +0200154 testcase TC_selftest() runs on dummy_CT {
155 const charstring c_auep := "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100156 const charstring c_mdcx3 := "MDCX 18983215 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n";
Harald Welte00a067f2017-09-13 23:27:17 +0200157 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
158 "I: 1\n" &
159 "\n" &
160 "v=0\r\n" &
161 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
162 "s=-\r\n" &
163 "c=IN IP4 0.0.0.0\r\n" &
164 "t=0 0\r\n" &
165 "m=audio 0 RTP/AVP 126\r\n" &
166 "a=rtpmap:126 AMR/8000\r\n" &
167 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100168 const charstring c_mdcx4 := "MDCX 18983216 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200169 "M: sendrecv\r" &
170 "C: 2\r\n" &
171 "I: 1\r\n" &
172 "L: p:20, a:AMR, nt:IN\r\n" &
173 "\n" &
174 "v=0\r\n" &
175 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800176 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200177 "c=IN IP4 0.0.0.0\r\n" &
178 "t=0 0\r\n" &
179 "m=audio 4441 RTP/AVP 99\r\n" &
180 "a=rtpmap:99 AMR/8000\r\n" &
181 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800182 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200183
184 log(c_auep);
185 log(dec_MgcpCommand(c_auep));
186
187 log(c_mdcx3);
188 log(dec_MgcpCommand(c_mdcx3));
189
190 log(c_mdcx3_ret);
191 log(dec_MgcpResponse(c_mdcx3_ret));
192
193 log(c_mdcx4);
194 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800195
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100196 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
197 log(enc_MgcpCommand(valueof(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H))));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800198
199 log(c_crcx510_ret);
200 log(dec_MgcpResponse(c_crcx510_ret));
201 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100202
203 /* We didn't encounter any DTE, so pass the test */
204 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800205 }
206
Harald Weltee636afd2017-09-17 16:24:09 +0800207 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100208 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800209 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
210 * - CRCX with remote session description and without
211 *
212 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100213 * x packetization != 20ms
214 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800215 * x unsupported mode (517)
216 * x bidirectional mode before RemoteConnDesc: 527
217 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100218 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800219 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
220 */
221
Harald Welte21ba5572017-09-19 17:55:05 +0800222 /* build a receive template for receiving a MGCP message. You
223 * pass the MGCP response template in, and it will generate an
224 * MGCP_RecvFrom template that can match the primitives arriving on the
225 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800226 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
227 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100228 connId := g_mgcp_conn_id,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200229 remName := mp_remote_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800230 remPort := mp_remote_udp_port,
Pau Espin Pedrolb604af02020-09-07 17:12:39 +0200231 locName := mp_local_ipv4,
Harald Weltee636afd2017-09-17 16:24:09 +0800232 locPort := mp_local_udp_port,
233 msg := { response := resp }
234 }
235 return mrf;
236 }
237
238 /* Send a MGCP request + receive a (matching!) response */
239 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
240 var MgcpMessage msg := { command := valueof(cmd) };
241 resp.line.trans_id := cmd.line.trans_id;
242 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800243 var MGCP_RecvFrom mrf;
244 timer T := 5.0;
245
Harald Welte55015362017-11-18 16:02:42 +0100246 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800247 T.start;
248 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800249 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200250 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
251 setverdict(fail, "Response didn't match template");
252 mtc.stop;
253 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800254 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200255 [] T.timeout {
256 setverdict(fail, "Timeout waiting for response to ", cmd);
257 mtc.stop;
258 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800259 }
260 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800261
262 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
263 return mrf.msg.response;
264 } else {
265 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
266 return r;
267 }
Harald Welte00a067f2017-09-13 23:27:17 +0200268 }
269
Harald Welteba62c8c2017-11-18 18:26:49 +0100270 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
271 var integer i;
272 for (i := 0; i < lengthof(resp.params); i := i + 1) {
273 var MgcpParameter par := resp.params[i];
274 if (par.code == "I") {
275 return str2hex(par.val);
276 }
277 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200278 setverdict(fail, "Could not find conn id for MgcpReponse");
279 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100280 return '00000000'H;
281 }
282
Harald Welte10889c12017-11-18 19:40:31 +0100283 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
284 template MgcpCallId call_id := omit,
285 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100286 var template MgcpCommand cmd;
287 var MgcpResponse resp;
288 var template MgcpResponse rtmpl := {
289 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100290 code := ret_code,
291 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100292 },
293 params := *,
294 sdp := *
295 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100296 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100297 resp := mgcp_transceive_mgw(cmd, rtmpl);
298 }
299
Harald Welte10889c12017-11-18 19:40:31 +0100300 /* Send DLCX and expect OK response */
301 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
302 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100303 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100304 }
305
Harald Welteba62c8c2017-11-18 18:26:49 +0100306 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100307 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100308 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100309 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100310 }
311
Harald Weltebb7523b2018-03-29 08:52:01 +0200312 type record HostPort {
313 charstring hostname,
314 integer portnr optional
315 }
316 type record RtpFlowData {
317 HostPort em, /* emulation side */
318 HostPort mgw, /* mgw side */
319 uint7_t pt,
320 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200321 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100322 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200323 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
324 MgcpOsmuxCID osmux_cid optional,
325 MgcpOsmuxCID osmux_cid_response optional,
326 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100327 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200328 }
329
Philipp Maier2321ef92018-06-27 17:52:04 +0200330 /* Create an RTP flow (bidirectional, or receive-only) */
331 function f_flow_create(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
Harald Weltebb7523b2018-03-29 08:52:01 +0200332 boolean one_phase := true)
333 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200334 var template MgcpCommand cmd;
335 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100336 var SDP_attribute_list attributes;
337
338 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
339 if (isvalue(flow.fmtp)) {
340 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
341 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200342
343 /* bind local RTP emulation socket */
344 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
345
Philipp Maier28bb8292018-07-20 17:09:17 +0200346 /* configure rtp-emulation */
347 if (ispresent(flow.rtp_cfg)) {
348 f_rtpem_configure(pt, flow.rtp_cfg);
349 } else {
350 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
351 rtp_cfg.tx_payload_type := flow.pt
352 f_rtpem_configure(pt, rtp_cfg);
353 }
354
Harald Weltebb7523b2018-03-29 08:52:01 +0200355 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200356 /* Connect flow to MGW using a CRCX that also contains an SDP
357 * part that tells the MGW where we are listening for RTP streams
358 * that come from the MGW. We get a fully working connection in
359 * one go. */
360
361 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200362 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100363 flow.em.portnr, { int2str(flow.pt) }, attributes);
364
Harald Weltebb7523b2018-03-29 08:52:01 +0200365 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
366 flow.mgcp_conn_id := extract_conn_id(resp);
367 /* extract port number from response */
368 flow.mgw.portnr :=
369 resp.sdp.media_list[0].media_field.ports.port_number;
370 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200371 /* Create a half-open connection only. We do not tell the MGW
372 * where it can send RTP streams to us. This means this
373 * connection will only be able to receive but can not send
374 * data back to us. In order to turn the connection in a fully
375 * bi-directional one, a separate MDCX is needed. */
376
377 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200378 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
379 flow.mgcp_conn_id := extract_conn_id(resp);
380 /* extract MGW-side port number from response */
381 flow.mgw.portnr :=
382 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200383 }
384 /* finally, connect the emulation-side RTP socket to the MGW */
385 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
386 }
387
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200388 /* Create an Osmux flow (bidirectional, or receive-only) */
389 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
390 boolean one_phase := true)
391 runs on dummy_CT {
392 var template MgcpCommand cmd;
393 var MgcpResponse resp;
394 var SDP_attribute_list attributes;
395 var OsmuxTxHandle tx_hdl;
396 var OsmuxRxHandle rx_hdl;
397 var charstring cid_response;
398 var OsmuxCID cid_resp_parsed
399
400 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
401 if (isvalue(flow.fmtp)) {
402 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
403 }
404
405 /* bind local Osmux emulation socket */
406 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
407
408 /* configure osmux-emulation */
409 if (ispresent(flow.osmux_cfg)) {
410 f_osmuxem_configure(pt, flow.osmux_cfg);
411 } else {
412 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
413 f_osmuxem_configure(pt, osmux_cfg);
414 flow.osmux_cfg := osmux_cfg
415 }
416
417 if (one_phase) {
418 /* Connect flow to MGW using a CRCX that also contains an SDP
419 * part that tells the MGW where we are listening for Osmux streams
420 * that come from the MGW. We get a fully working connection in
421 * one go. */
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200422 if (flow.osmux_cid != -1) {
423 /* We may still want to negotiate osmux CID later at MDCX */
424 rx_hdl := c_OsmuxemDefaultRxHandle;
425 rx_hdl.cid := flow.osmux_cid;
426 f_osmuxem_register_rxhandle(pt, rx_hdl);
427 flow.osmux_cid_sent := true;
428 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200429 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
430 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
431 flow.em.portnr, { int2str(flow.pt) }, attributes);
432 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
433 flow.mgcp_conn_id := extract_conn_id(resp);
434 /* extract port number from response */
435 flow.mgw.portnr :=
436 resp.sdp.media_list[0].media_field.ports.port_number;
437 } else {
438 /* Create a half-open connection only. We do not tell the MGW
439 * where it can send Osmux streams to us. This means this
440 * connection will only be able to receive but can not send
441 * data back to us. In order to turn the connection in a fully
442 * bi-directional one, a separate MDCX is needed. */
443
444 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
445 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
446
447 flow.mgcp_conn_id := extract_conn_id(resp);
448 /* extract MGW-side port number from response */
449 flow.mgw.portnr :=
450 resp.sdp.media_list[0].media_field.ports.port_number;
451 }
452
453 /* extract Osmux CID we got assigned by the MGW */
454 var MgcpMessage resp_msg := {
455 response := resp
456 }
457
458 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
459 setverdict(fail, "No Osmux CID in MGCP response", resp);
460 mtc.stop;
461 }
462
463 /* Make sure response is no wildcard */
464 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
465 if (flow.osmux_cid_response == -1) {
466 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
467 mtc.stop;
468 }
469 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
470 f_osmuxem_register_txhandle(pt, tx_hdl);
471
472 /* finally, connect the emulation-side RTP socket to the MGW */
473 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
474 }
475
Philipp Maier2321ef92018-06-27 17:52:04 +0200476 /* Modify an existing RTP flow */
477 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
478 runs on dummy_CT {
479 var template MgcpCommand cmd;
480 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100481 var SDP_attribute_list attributes;
482
483 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
484 if (isvalue(flow.fmtp)) {
485 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
486 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200487
488 /* rebind local RTP emulation socket to the new address */
489 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
490
Philipp Maier28bb8292018-07-20 17:09:17 +0200491 /* reconfigure rtp-emulation */
492 if (ispresent(flow.rtp_cfg)) {
493 f_rtpem_configure(pt, flow.rtp_cfg);
494 } else {
495 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
496 rtp_cfg.tx_payload_type := flow.pt
497 f_rtpem_configure(pt, rtp_cfg);
498 }
499
Philipp Maier2321ef92018-06-27 17:52:04 +0200500 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
501 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
502 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100503 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200504 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
505
506 /* extract MGW-side port number from response. (usually this
507 * will not change, but thats is up to the MGW) */
508 flow.mgw.portnr :=
509 resp.sdp.media_list[0].media_field.ports.port_number;
510
511 /* reconnect the emulation-side RTP socket to the MGW */
512 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
513 }
514
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200515 /* Modify an existing Osmux flow */
516 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
517 runs on dummy_CT {
518 var template MgcpCommand cmd;
519 var MgcpResponse resp;
520 var SDP_attribute_list attributes;
521 var OsmuxRxHandle rx_hdl;
522 var charstring cid_response;
523 var OsmuxCID cid_resp_parsed
524
525 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
526 if (isvalue(flow.fmtp)) {
527 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
528 }
529
530 /* rebind local Osmux emulation socket to the new address */
531 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
532
533 /* configure osmux-emulation */
534 if (ispresent(flow.osmux_cfg)) {
535 f_osmuxem_configure(pt, flow.osmux_cfg);
536 } else {
537 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
538 f_osmuxem_configure(pt, osmux_cfg);
539 }
540
541 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
542 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
543 rx_hdl := c_OsmuxemDefaultRxHandle;
544 rx_hdl.cid := flow.osmux_cid;
545 f_osmuxem_register_rxhandle(pt, rx_hdl);
546 flow.osmux_cid_sent := true;
547 }
548
549 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
550 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
551 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
552 flow.em.portnr, { int2str(flow.pt) }, attributes);
553 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
554
555 /* extract MGW-side port number from response. (usually this
556 * will not change, but thats is up to the MGW) */
557 flow.mgw.portnr :=
558 resp.sdp.media_list[0].media_field.ports.port_number;
559
560 /* extract Osmux CID we got assigned by the MGW */
561 var MgcpMessage resp_msg := {
562 response := resp
563 }
564
565 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
566 setverdict(fail, "No Osmux CID in MGCP response", resp);
567 mtc.stop;
568 }
569
570 /* Make sure response is no wildcard */
571 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
572 if (cid_resp_parsed == -1) {
573 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
574 mtc.stop;
575 }
576 if (cid_resp_parsed != flow.osmux_cid_response) {
577 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
578 mtc.stop;
579 }
580
581 /* reconnect the emulation-side Osmux socket to the MGW */
582 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
583 }
584
Philipp Maier2321ef92018-06-27 17:52:04 +0200585 /* Delete an existing RTP flow */
586 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
587 runs on dummy_CT {
588 var template MgcpCommand cmd;
589 var MgcpResponse resp;
590
591 /* Switch off RTP flow */
592 f_rtpem_mode(pt, RTPEM_MODE_NONE);
593
594 /* Delete connection on MGW (if needed) */
595 if (isvalue(call_id) and isvalue(ep)) {
596 f_sleep(0.1);
597 f_dlcx_ok(valueof(ep), call_id);
598 }
599 }
600
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200601 /* Delete an existing Osmux flow */
602 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
603 runs on dummy_CT {
604 var template MgcpCommand cmd;
605 var MgcpResponse resp;
606
607 /* Switch off Osmux flow */
608 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
609
610 /* Delete connection on MGW (if needed) */
611 if (isvalue(call_id) and isvalue(ep)) {
612 f_sleep(0.1);
613 f_dlcx_ok(valueof(ep), call_id);
614 }
615 }
616
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100617 function f_crcx(charstring ep_prefix) runs on dummy_CT {
618 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800619 var template MgcpCommand cmd;
620 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100621 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800622
Harald Welteedc45c12017-11-18 19:15:05 +0100623 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800624
Harald Welteba62c8c2017-11-18 18:26:49 +0100625 /* create the connection on the MGW */
626 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100627 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100628 extract_conn_id(resp);
629
630 /* clean-up */
631 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100632 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100633
Philipp Maier45635f42018-06-05 17:28:02 +0200634 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
635 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
636 var template MgcpCommand cmd;
637 var MgcpResponse resp;
638 var MgcpCallId call_id := '1234'H;
639
640 f_init(ep);
641
642 /* create the connection on the MGW */
643 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
644 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
645 extract_conn_id(resp);
646
647 /* clean-up */
648 f_dlcx_ok(ep, call_id);
649
650 /* See also OS#2658: Even when we omit the LCO information, we
651 expect the MGW to pick a sane payload type for us. This
652 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200653 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200654 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200655 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200656 }
657
658 /* See also OS#2658: We also expect the MGW to assign a port
659 number to us. */
660 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
661 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200662 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200663 }
664 }
665
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200666 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
667 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
668 var template MgcpCommand cmd;
669 var MgcpResponse resp;
670 var MgcpCallId call_id := '1234'H;
671 var charstring cid_response;
672
673 if (run_init) {
674 f_init(ep, true);
675 }
676
677 /* create the connection on the MGW */
678 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
679 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
680 extract_conn_id(resp);
681
682 /* extract Osmux CID we got assigned by the MGW */
683 var MgcpMessage resp_msg := {
684 response := resp
685 }
686
687 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
688 setverdict(fail, "No Osmux CID in MGCP response", resp);
689 mtc.stop;
690 }
691
692 /* Make sure response is no wildcard */
693 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
694 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
695 mtc.stop;
696 }
697
698 /* clean-up */
699 f_dlcx_ok(ep, call_id);
700 }
701
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100702 /* test valid CRCX without SDP */
703 testcase TC_crcx() runs on dummy_CT {
704 f_crcx(c_mgw_ep_rtpbridge);
705 setverdict(pass);
706 }
707
Philipp Maier45635f42018-06-05 17:28:02 +0200708 /* test valid CRCX without SDP and LCO */
709 testcase TC_crcx_no_lco() runs on dummy_CT {
710 f_crcx_no_lco(c_mgw_ep_rtpbridge);
711 setverdict(pass);
712 }
713
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100714 /* test valid CRCX without SDP (older method without endpoint prefix) */
715 testcase TC_crcx_noprefix() runs on dummy_CT {
716 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800717 setverdict(pass);
718 }
719
720 /* test CRCX with unsupported mode, expect 517 */
721 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
722 var template MgcpCommand cmd;
723 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100724 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100725 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800726 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
727
Harald Welteedc45c12017-11-18 19:15:05 +0100728 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800729
Harald Welteba62c8c2017-11-18 18:26:49 +0100730 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800731 resp := mgcp_transceive_mgw(cmd, rtmpl);
732 setverdict(pass);
733 }
734
Harald Welte21ba5572017-09-19 17:55:05 +0800735 /* test CRCX with early bi-directional mode, expect 527 as
736 * bi-diretional media can only be established once both local and
737 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800738 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
739 var template MgcpCommand cmd;
740 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100741 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100742 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800743 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
744
Harald Welteedc45c12017-11-18 19:15:05 +0100745 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800746
Harald Welteba62c8c2017-11-18 18:26:49 +0100747 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800748 resp := mgcp_transceive_mgw(cmd, rtmpl);
749 setverdict(pass);
750 }
751
752 /* test CRCX with unsupported Parameters */
753 testcase TC_crcx_unsupp_param() runs on dummy_CT {
754 var template MgcpCommand cmd;
755 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100756 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100757 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800758 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
759
Harald Welteedc45c12017-11-18 19:15:05 +0100760 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800761
Harald Welteba62c8c2017-11-18 18:26:49 +0100762 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100763 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
764 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
765
Harald Weltee636afd2017-09-17 16:24:09 +0800766 resp := mgcp_transceive_mgw(cmd, rtmpl);
767 setverdict(pass);
768 }
769
770 /* test CRCX with missing CallId */
771 testcase TC_crcx_missing_callid() runs on dummy_CT {
772 var template MgcpCommand cmd;
773 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100774 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100775 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800776
Harald Welteedc45c12017-11-18 19:15:05 +0100777 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800778
Harald Welteba62c8c2017-11-18 18:26:49 +0100779 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800780 cmd.params := {
781 t_MgcpParConnMode("recvonly"),
782 t_MgcpParLocConnOpt("p:20")
783 }
784 resp := mgcp_transceive_mgw(cmd, rtmpl);
785 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100786
Harald Weltee636afd2017-09-17 16:24:09 +0800787 }
788
789 /* test CRCX with missing Mode */
790 testcase TC_crcx_missing_mode() runs on dummy_CT {
791 var template MgcpCommand cmd;
792 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100793 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100794 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100795 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800796
Harald Welteedc45c12017-11-18 19:15:05 +0100797 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800798
Harald Welteba62c8c2017-11-18 18:26:49 +0100799 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800800 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100801 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800802 t_MgcpParLocConnOpt("p:20")
803 }
804 resp := mgcp_transceive_mgw(cmd, rtmpl);
805 setverdict(pass);
806 }
807
808 /* test CRCX with unsupported packetization interval */
809 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
810 var template MgcpCommand cmd;
811 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100812 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100813 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100814 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800815
Harald Welteedc45c12017-11-18 19:15:05 +0100816 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800817
Harald Welteba62c8c2017-11-18 18:26:49 +0100818 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100819 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800820 resp := mgcp_transceive_mgw(cmd, rtmpl);
821 setverdict(pass);
822 }
823
824 /* test CRCX with illegal double presence of local connection option */
825 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
826 var template MgcpCommand cmd;
827 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100828 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100829 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800830 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
831
Harald Welteedc45c12017-11-18 19:15:05 +0100832 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800833
Harald Welteba62c8c2017-11-18 18:26:49 +0100834 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100835 /* p:20 is permitted only once and not twice! */
836 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800837 resp := mgcp_transceive_mgw(cmd, rtmpl);
838 setverdict(pass);
839 }
840
841 /* test valid CRCX with valid SDP */
842 testcase TC_crcx_sdp() runs on dummy_CT {
843 var template MgcpCommand cmd;
844 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100845 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100846 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800847
Harald Welteedc45c12017-11-18 19:15:05 +0100848 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800849
Harald Welteba62c8c2017-11-18 18:26:49 +0100850 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800851 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
852 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
853 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100854 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100855
856 /* clean-up */
857 f_dlcx_ok(ep, call_id);
858
Harald Weltee636afd2017-09-17 16:24:09 +0800859 setverdict(pass);
860 }
861
Philipp Maier5e06cee2018-02-01 18:28:08 +0100862 /* test valid wildcarded CRCX */
863 testcase TC_crcx_wildcarded() runs on dummy_CT {
864 var template MgcpCommand cmd;
865 var MgcpResponse resp;
866 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
867 var MgcpCallId call_id := '1234'H;
868 var MgcpEndpoint ep_assigned;
869 f_init();
870
871 /* create the connection on the MGW */
872 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
873 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
874 extract_conn_id(resp);
875
876 /* extract endpoint name we got assigned by the MGW */
877 var MgcpMessage resp_msg := {
878 response := resp
879 }
880 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
881 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200882 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100883 }
884
885 /* clean-up */
886 f_dlcx_ok(ep_assigned, call_id);
887
888 setverdict(pass);
889 }
890
891 /* test valid wildcarded CRCX */
892 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maierc60e8472020-07-08 12:57:13 +0200893 const integer n_endpoints := 31;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100894 var integer i;
895 var template MgcpCommand cmd;
896 var MgcpResponse resp;
897 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
898 var MgcpCallId call_id := '1234'H;
899 var MgcpEndpoint ep_assigned[n_endpoints];
900 f_init();
901
902 /* Exhaust all endpoint resources on the virtual trunk */
903 for (i := 0; i < n_endpoints; i := i+1) {
904 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
905 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
906
907 /* Make sure we got a connection id */
908 extract_conn_id(resp);
909
910 var MgcpMessage resp_msg := {
911 response := resp
912 }
913 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
914 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200915 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100916 }
917 }
918
919 /* Try to allocate one more endpoint, which should fail */
920 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
921 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
922 resp := mgcp_transceive_mgw(cmd, rtmpl);
923 setverdict(pass);
924
925 /* clean-up */
926 for (i := 0; i < n_endpoints; i := i+1) {
927 f_dlcx_ok(ep_assigned[i], call_id);
928 }
929 setverdict(pass);
930 }
931
Harald Weltee636afd2017-09-17 16:24:09 +0800932 /* TODO: various SDP related bits */
933
934
935 /* TODO: CRCX with X-Osmux */
936 /* TODO: double CRCX without force_realloc */
937
938 /* TODO: MDCX (various) */
939
940 /* TODO: MDCX without CRCX first */
941 testcase TC_mdcx_without_crcx() runs on dummy_CT {
942 var template MgcpCommand cmd;
943 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100944 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100945 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800946 var template MgcpResponse rtmpl := {
947 line := {
948 /* TODO: accept/enforce better error? */
949 code := "400",
950 string := ?
951 },
952 params:= { },
953 sdp := omit
954 };
955
Harald Welteedc45c12017-11-18 19:15:05 +0100956 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800957
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100958 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800959 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
960 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
961 valueof(ts_SDP_ptime(20)) });
962 resp := mgcp_transceive_mgw(cmd, rtmpl);
963 setverdict(pass);
964 }
965
966 /* DLCX without CRCX first */
967 testcase TC_dlcx_without_crcx() runs on dummy_CT {
968 var template MgcpCommand cmd;
969 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100970 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800971 var template MgcpResponse rtmpl := {
972 line := {
Harald Weltef91edf32017-12-28 14:16:21 +0100973 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +0800974 string := ?
975 },
976 params:= { },
977 sdp := omit
978 };
979
Harald Welteedc45c12017-11-18 19:15:05 +0100980 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800981
Harald Welteedc45c12017-11-18 19:15:05 +0100982 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800983 resp := mgcp_transceive_mgw(cmd, rtmpl);
984 setverdict(pass);
985 }
986
Philipp Maier8a3dc922018-02-02 14:55:12 +0100987 /* test valid wildcarded MDCX */
988 testcase TC_mdcx_wildcarded() runs on dummy_CT {
989 /* Note: A wildcarded MDCX is not allowed, so we expect the
990 * MGW to reject this request */
991 var template MgcpCommand cmd;
992 var MgcpResponse resp;
993 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
994 var MgcpCallId call_id := '1225'H;
995 var template MgcpResponse rtmpl := {
996 line := {
997 /* TODO: accept/enforce better error? */
998 code := "507",
999 string := ?
1000 },
1001 params:= { },
1002 sdp := omit
1003 };
1004
1005 f_init(ep);
1006
1007 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1008 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1009 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1010 valueof(ts_SDP_ptime(20)) });
1011 resp := mgcp_transceive_mgw(cmd, rtmpl);
1012 setverdict(pass);
1013 }
1014
1015 /* test valid wildcarded DLCX */
1016 testcase TC_dlcx_wildcarded() runs on dummy_CT {
1017 /* Note: A wildcarded DLCX is specified, but our MGW does not
1018 * support this feature so we expect the MGW to reject the
1019 * request */
1020 var template MgcpCommand cmd;
1021 var MgcpResponse resp;
1022 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1023 var template MgcpResponse rtmpl := {
1024 line := {
1025 code := "507",
1026 string := ?
1027 },
1028 params:= { },
1029 sdp := omit
1030 };
1031
1032 f_init(ep);
1033
1034 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1035 resp := mgcp_transceive_mgw(cmd, rtmpl);
1036 setverdict(pass);
1037 }
1038
Harald Welte79181ff2017-11-18 19:26:11 +01001039 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1040 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1041 var template MgcpCommand cmd;
1042 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001043 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001044 var MgcpCallId call_id := '51234'H;
1045
1046 f_init(ep);
1047
1048 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1049 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1050
1051 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1052
1053 setverdict(pass);
1054 }
1055
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001056 /* test valid CRCX without SDP */
1057 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1058 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1059 setverdict(pass);
1060 }
1061
1062 /* test valid CRCX without SDP */
1063 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1064 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1065 setverdict(pass);
1066 }
1067
1068 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1069 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1070 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1071 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1072 setverdict(pass);
1073 }
1074
1075 /* Create one half open connection in receive-only mode. The MGW must accept
1076 * the packets but must not send any. */
1077 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1078 var RtpFlowData flow;
1079 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1080 var MgcpCallId call_id := '1225'H;
1081 var OsmuxemStats stats;
1082 var OsmuxTxHandle tx_hdl;
1083
1084 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001085 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001086 flow.em.portnr := mp_local_osmux_port;
1087 flow.osmux_cid := -1;
1088 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1089
1090 /* create a transmitter not yet known by MGW */
1091 tx_hdl := valueof(t_TxHandleAMR590(2));
1092 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1093
1094 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1095 f_sleep(1.0);
1096 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1097
1098 stats := f_osmuxem_stats_get(OsmuxEM);
1099
1100 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1101 setverdict(fail);
1102 }
1103 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1104 setverdict(fail);
1105 }
1106
1107 f_osmuxem_stats_err_check(stats);
1108
1109 setverdict(pass);
1110 }
1111
1112 /* Create one connection in loopback mode, test if the Osmux packets are
1113 * actually reflected */
1114 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1115 var RtpFlowData flow;
1116 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1117 var MgcpCallId call_id := '1225'H;
1118 var OsmuxemStats stats;
1119 var OsmuxTxHandle tx_hdl;
1120
1121 f_init(ep, true);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001122 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001123 flow.em.portnr := mp_local_osmux_port;
1124 flow.osmux_cid := 2;
1125 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1126
1127 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1128 f_sleep(1.0);
1129
1130 /* Switch off both Tx, wait to receive delayed frames from MGW */
1131 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1132 f_sleep(0.1);
1133 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1134
1135 stats := f_osmuxem_stats_get(OsmuxEM);
1136
1137 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1138 setverdict(fail);
1139 }
1140 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1141 setverdict(fail);
1142 }
1143
1144 f_osmuxem_stats_err_check(stats);
1145
1146 setverdict(pass);
1147 }
1148
1149 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1150 * must match the reception statistics on the other side and vice versa. The
1151 * user may also supply a tolerance value (number of packets) when deviations
1152 * are acceptable */
1153 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1154 var integer plen;
1155
1156 log("stats A: ", a);
1157 log("stats B: ", b);
1158 log("tolerance: ", tolerance, " packets");
1159 log("batch_size: ", batch_size, " packets");
1160
1161 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1162
1163 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1164 return false;
1165 }
1166
1167 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1168 return false;
1169 }
1170
1171 if(a.num_pkts_tx > 0) {
1172 plen := a.bytes_payload_tx / a.num_pkts_tx;
1173 } else {
1174 plen := 0;
1175 }
1176
1177 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1178 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1179 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1180 return false;
1181 }
1182
1183 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) {
1184 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1185 return false;
1186 }
1187
1188 return true;
1189 }
1190
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001191 function f_TC_two_crcx_and_rtp_osmux(boolean bidir,
1192 charstring local_ip_rtp, charstring remote_ip_rtp,
1193 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001194 var RtpFlowData flow[2];
1195 var RtpemStats stats_rtp;
1196 var OsmuxemStats stats_osmux;
1197 var MgcpResponse resp;
1198 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1199 var MgcpCallId call_id := '1226'H;
1200 var integer tolerance := 0;
1201
1202 f_init(ep, true);
1203
1204 /* from us to MGW */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001205 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001206 flow[0].rtp_cfg := c_RtpemDefaultCfg
1207 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1208 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1209 flow[0].rtp_cfg.rx_fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload);
1210 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1211 /* bind local RTP emulation sockets */
1212 flow[0].em.portnr := 10000;
1213 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1214
1215 /* from MGW back to us */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001216 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001217 flow[1].em.portnr := mp_local_osmux_port;
1218 flow[1].osmux_cid := 2;
1219 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1220 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1221
1222 if (bidir) {
1223 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1224 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1225
1226 /* Note: When we test bidirectional we may
1227 * loose packets during switch off because
1228 * both ends are transmitting and we only
1229 * can switch them off one by one. */
1230 tolerance := 3;
1231 } else {
1232 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1233 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1234 }
1235
1236 f_sleep(1.0);
1237
1238 /* Switch off both Tx, wait to receive delayed frames from MGW */
1239 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1240 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1241 f_sleep(0.1);
1242
1243 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1244 f_flow_delete(RTPEM[1]);
1245
1246 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1247 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1248 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1249 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1250 mtc.stop;
1251 }
1252
1253 f_rtpem_stats_err_check(stats_rtp);
1254 f_osmuxem_stats_err_check(stats_osmux);
1255
1256 setverdict(pass);
1257 }
1258
1259 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1260 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001261 f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1262 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001263 }
1264
1265 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1266 * exchange some data in both directions */
1267 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001268 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1269 mp_local_ipv4, mp_remote_ipv4);
1270 }
1271
1272 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */
1273 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT {
1274 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1275 mp_local_ipv6, mp_remote_ipv6);
1276 }
1277 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */
1278 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT {
1279 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1280 mp_local_ipv6, mp_remote_ipv6);
1281 }
1282 /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */
1283 testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT {
1284 f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6,
1285 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001286 }
1287
1288
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001289 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard,
1290 charstring local_ip_rtp, charstring remote_ip_rtp,
1291 charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT {
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001292 var RtpFlowData flow[2];
1293 var RtpemStats stats_rtp;
1294 var OsmuxemStats stats_osmux;
1295 var MgcpResponse resp;
1296 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1297 var MgcpCallId call_id := '1227'H;
1298 var integer num_pkts_tx[2];
1299 var integer temp;
1300
1301 f_init(ep, true);
1302
1303 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001304 flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001305 flow[0].rtp_cfg := c_RtpemDefaultCfg
1306 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1307 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1308 flow[0].rtp_cfg.rx_fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload);
1309 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1310 /* bind local RTP emulation sockets */
1311 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001312 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001313
1314
1315 /* Create the second connection. This connection will be also
1316 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001317 flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000"));
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001318 flow[1].em.portnr := mp_local_osmux_port;
1319 if (crcx_osmux_wildcard) {
1320 flow[1].osmux_cid := -1;
1321 } else {
1322 flow[1].osmux_cid := 2;
1323 }
1324 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001325 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001326
1327
1328 /* The first leg starts transmitting */
1329 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1330 f_sleep(0.5);
1331 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1332 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1333 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1334 mtc.stop;
1335 }
1336 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1337 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1338 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1339 mtc.stop;
1340 }
1341
1342 /* The second leg starts transmitting a little later */
1343 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1344 f_sleep(1.0);
1345 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1346 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1347 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1348 mtc.stop;
1349 }
1350 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1351 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1352 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1353 mtc.stop;
1354 }
1355
1356 /* The first leg will now be switched into bidirectional
1357 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001358 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1359 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1360 num_pkts_tx[1] := stats_osmux.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001361 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1362 f_sleep(0.5);
1363 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1364 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1365 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1366 mtc.stop;
1367 }
1368 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1369 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1370 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1371 mtc.stop;
1372 }
1373
1374 /* When the second leg is switched into bidirectional mode
1375 * as well, then the MGW will connect the two together and
1376 * we should see RTP streams passing through from both ends. */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001377 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1378 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrole67b1c62020-09-08 12:16:17 +02001379 num_pkts_tx[0] := stats_rtp.num_pkts_tx;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001380
1381 if (crcx_osmux_wildcard) {
1382 /* For now we must set same CID as the MGW recvCID,
1383 * having sendCID!=recvCID is not yet supported. */
1384 flow[1].osmux_cid := flow[1].osmux_cid_response;
1385 }
1386 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1387 f_sleep(2.0);
1388
1389 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1390 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1391
1392 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1393 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1394 log("stats_rtp: ", stats_rtp);
1395 log("stats_osmux: ", stats_osmux);
1396 log("old_rtp_tx: ", num_pkts_tx[0]);
1397 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1398 mtc.stop;
1399 }
1400
1401 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1402 if (temp > 3 or temp < -3) {
1403 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1404 mtc.stop;
1405 }
1406
1407 f_rtpem_stats_err_check(stats_rtp);
1408 f_osmuxem_stats_err_check(stats_osmux);
1409
1410 /* Tear down */
1411 f_flow_delete(RTPEM[0]);
1412 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1413 setverdict(pass);
1414 }
1415
1416 /* create one RTP and one OSmux emulations and pass data in both
1417 directions. Create CRCX with wildcard Osmux CID and set it later
1418 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1419 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001420 f_two_crcx_mdcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4,
1421 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001422 }
1423
1424 /* create one RTP and one OSmux emulations and pass data in both
1425 directions. Create CRCX with fixed Osmux CID and keep it during
1426 MDCX. This is similar to how BSC sets up the call in AoIP. */
1427 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001428 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1429 mp_local_ipv4, mp_remote_ipv4);
1430 }
1431
1432 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6. */
1433 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6() runs on dummy_CT {
1434 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1435 mp_local_ipv6, mp_remote_ipv6);
1436 }
1437 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv4 (RTP) and IPv6 (Osmux). */
1438 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6() runs on dummy_CT {
1439 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4,
1440 mp_local_ipv6, mp_remote_ipv6);
1441 }
1442 /* Same as TC_two_crcx_mdcx_and_rtp_osmux_wildcard, but using IPv6 (RTP) and IPv4 (Osmux). */
1443 testcase TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4() runs on dummy_CT {
1444 f_two_crcx_mdcx_and_rtp_osmux(false, mp_local_ipv6, mp_remote_ipv6,
1445 mp_local_ipv4, mp_remote_ipv4);
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001446 }
1447
Harald Welte646ecdb2017-12-28 03:21:57 +01001448 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1449 var template MgcpCommand cmd;
1450 var MgcpResponse resp;
1451
1452 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1453 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1454
1455 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1456
1457 setverdict(pass);
1458 }
1459
1460 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1461 var MgcpEndpoint ep;
1462 var MgcpCallId call_id;
1463 var integer ep_nr;
1464
1465 f_init();
1466
1467 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001468 if(ep_nr > 15) {
1469 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1470 } else {
1471 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1472 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001473 call_id := int2hex(ep_nr, 2) & '1234'H;
1474 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1475 }
1476 }
1477
Harald Welte79181ff2017-11-18 19:26:11 +01001478 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1479 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001480 var template MgcpCommand cmd;
1481 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001482 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001483 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001484
Harald Welteedc45c12017-11-18 19:15:05 +01001485 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001486
Harald Welteba62c8c2017-11-18 18:26:49 +01001487 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001488 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001489
Harald Welteba62c8c2017-11-18 18:26:49 +01001490 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001491
1492 setverdict(pass);
1493 }
1494
Harald Welte79181ff2017-11-18 19:26:11 +01001495 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1496 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1497 var template MgcpCommand cmd;
1498 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001499 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001500 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001501
1502 f_init(ep);
1503
1504 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1505 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1506
1507 f_dlcx_ok(ep);
1508
1509 setverdict(pass);
1510 }
1511
1512
Harald Welte6d167f82017-11-18 19:41:35 +01001513 /* CRCX + DLCX of valid endpoint but invalid call-id */
1514 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1515 var template MgcpCommand cmd;
1516 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001517 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001518 var MgcpCallId call_id := '51231'H;
1519
1520 f_init(ep);
1521
1522 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1523 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1524
1525 f_dlcx(ep, "516", *, 'ffff'H);
1526
1527 setverdict(pass);
1528 }
1529
1530
1531 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1532 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1533 var template MgcpCommand cmd;
1534 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001535 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001536 var MgcpCallId call_id := '51230'H;
1537
1538 f_init(ep);
1539
1540 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1541 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1542
1543 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1544
1545 setverdict(pass);
1546 }
1547
1548
Harald Weltee636afd2017-09-17 16:24:09 +08001549 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001550 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1551 var template MgcpCommand cmd;
1552 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001553 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001554 var MgcpCallId call_id := '51229'H;
1555 var template MgcpResponse rtmpl := {
1556 line := {
1557 code := "200",
1558 string := "OK"
1559 },
1560 params:= { },
1561 sdp := omit
1562 };
1563
1564 f_init(ep);
1565
1566 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1567 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1568
1569 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1570 resp := mgcp_transceive_mgw(cmd, rtmpl);
1571 resp := mgcp_transceive_mgw(cmd, rtmpl);
1572
1573 setverdict(pass);
1574 }
1575
Harald Weltebb7523b2018-03-29 08:52:01 +02001576 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1577 charstring codec) := {
1578 em := {
1579 hostname := host_a,
1580 portnr := omit
1581 },
1582 mgw := {
1583 hostname := host_b,
1584 portnr := omit
1585 },
1586 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001587 codec := codec,
1588 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001589 }
Harald Weltef53f1642017-11-18 19:57:11 +01001590
Harald Weltebb7523b2018-03-29 08:52:01 +02001591 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1592 testcase TC_rtpem_selftest() runs on dummy_CT {
1593 var RtpemStats stats[2];
1594 var integer local_port := 10000;
1595 var integer local_port2 := 20000;
1596
1597 f_init();
1598
1599 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1600 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1601
1602 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1603 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1604
1605 log("=== starting");
1606 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1607 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1608
1609 f_sleep(5.0);
1610
1611 log("=== stopping");
1612 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1613 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1614 f_sleep(0.5);
1615 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1616 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1617
1618 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1619 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1620 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1621 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001622 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001623 }
1624 setverdict(pass);
1625 }
1626
Philipp Maier2321ef92018-06-27 17:52:04 +02001627 /* Create one half open connection in receive-only mode. The MGW must accept
1628 * the packets but must not send any. */
1629 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1630 var RtpFlowData flow;
1631 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1632 var MgcpCallId call_id := '1225'H;
1633 var RtpemStats stats;
1634
1635 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001636 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001637 flow.em.portnr := 10000;
Pau Espin Pedrol25f47a72020-09-08 18:10:54 +02001638 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001639
1640 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1641 f_sleep(1.0);
1642 f_flow_delete(RTPEM[0], ep, call_id);
1643
1644 stats := f_rtpem_stats_get(RTPEM[0]);
1645
Philipp Maier42b17cc2019-10-01 13:53:17 +02001646 /* Make sure that at least some amount of RTP packets/bytes
1647 * have has been transmitted. The compare values for
1648 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1649 * using a testrun and the results were devided by 2, so even
1650 * in load situations we should reach the minimum amount of
1651 * required packets/bytes */
1652
1653 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001654 setverdict(fail);
1655 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001656 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001657 setverdict(fail);
1658 }
Philipp Maier36291392018-07-25 09:40:44 +02001659
1660 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001661
1662 setverdict(pass);
1663 }
1664
1665 /* Create one connection in loopback mode, test if the RTP packets are
1666 * actually reflected */
1667 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
1668 var RtpFlowData flow;
1669 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1670 var MgcpCallId call_id := '1225'H;
1671 var RtpemStats stats;
1672
1673 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001674 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001675 flow.em.portnr := 10000;
1676 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
1677
1678 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1679 f_sleep(1.0);
1680 f_flow_delete(RTPEM[0], ep, call_id);
1681
1682 stats := f_rtpem_stats_get(RTPEM[0]);
1683
1684 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1685 setverdict(fail);
1686 }
1687 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1688 setverdict(fail);
1689 }
Philipp Maier36291392018-07-25 09:40:44 +02001690
1691 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001692
1693 setverdict(pass);
1694 }
1695
Philipp Maier7df85f62018-07-25 10:26:09 +02001696 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1697 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001698 var RtpFlowData flow[2];
1699 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001700 var MgcpResponse resp;
1701 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1702 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001703 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001704
1705 f_init(ep);
1706
1707 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001708 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001709 /* bind local RTP emulation sockets */
1710 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001711 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001712
1713 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001714 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001715 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001716 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1717
1718 if (bidir) {
1719 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1720 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1721
1722 /* Note: When we test bidirectional we may
1723 * loose packets during switch off because
1724 * both ends are transmitting and we only
1725 * can switch them off one by one. */
1726 tolerance := 3;
1727 } else {
1728 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1729 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1730 }
1731
1732 f_sleep(1.0);
1733
1734 f_flow_delete(RTPEM[1]);
1735 f_flow_delete(RTPEM[0], ep, call_id);
1736
1737 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1738 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1739 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1740 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001741 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001742 }
1743
Philipp Maier36291392018-07-25 09:40:44 +02001744 f_rtpem_stats_err_check(stats[0]);
1745 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001746
Philipp Maier2321ef92018-06-27 17:52:04 +02001747 setverdict(pass);
1748 }
1749
1750 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1751 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001752 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001753 }
1754
1755 /* create two local RTP emulations; create two connections on MGW EP,
1756 * exchange some data in both directions */
1757 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001758 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1759 }
1760
1761 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1762 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1763 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1764 }
1765
1766 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1767 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1768 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001769 }
1770
1771 /* create two local RTP emulations and pass data in both directions */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001772 function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a,
1773 charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT {
Philipp Maier2321ef92018-06-27 17:52:04 +02001774 var RtpFlowData flow[2];
1775 var RtpemStats stats[2];
1776 var MgcpResponse resp;
1777 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1778 var MgcpCallId call_id := '1227'H;
1779 var integer num_pkts_tx[2];
1780 var integer temp;
1781
1782 f_init(ep);
1783
1784 /* Create the first connection in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001785 flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001786 flow[0].em.portnr := 10000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001787 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001788
1789 /* Create the second connection. This connection will be also
1790 * in receive only mode */
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001791 flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001792 flow[1].em.portnr := 20000;
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001793 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
Philipp Maier2321ef92018-06-27 17:52:04 +02001794
1795 /* The first leg starts transmitting */
1796 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1797 f_sleep(0.5);
1798 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1799 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001800 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001801 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001802 }
1803 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1804 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001805 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001806 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001807 }
1808
1809 /* The second leg starts transmitting a little later */
1810 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1811 f_sleep(1.0);
1812 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1813 if (stats[0].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001814 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001815 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001816 }
1817 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1818 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001819 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001820 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001821 }
1822
1823 /* The first leg will now be switched into bidirectional
1824 * mode, but we do not expect any data comming back yet. */
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001825 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1826 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1827 num_pkts_tx[1] := stats[1].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001828 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1829 f_sleep(0.5);
1830 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001831 if (stats[0].num_pkts_rx_err_disabled != 0) {
1832 setverdict(fail, "received packets from MGW on recvonly connection 0");
Daniel Willmannafce8662018-07-06 23:11:32 +02001833 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001834 }
1835 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1836 if (stats[1].num_pkts_rx_err_disabled != 0) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001837 setverdict(fail, "received packets from MGW on recvonly connection 1");
Daniel Willmannafce8662018-07-06 23:11:32 +02001838 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001839 }
1840
1841 /* When the second leg is switched into bidirectional mode
1842 * as well, then the MGW will connect the two together and
1843 * we should see RTP streams passing through from both ends. */
Philipp Maier2321ef92018-06-27 17:52:04 +02001844 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1845 stats[0] := f_rtpem_stats_get(RTPEM[0]);
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001846 num_pkts_tx[0] := stats[0].num_pkts_tx;
Philipp Maier2321ef92018-06-27 17:52:04 +02001847 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1848 f_sleep(2.0);
1849
1850 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1851 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1852
1853 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1854 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001855 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001856 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001857 }
1858
1859 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1860 if (temp > 3 or temp < -3) {
Pau Espin Pedrol89c76d22020-09-07 18:42:04 +02001861 setverdict(fail, "number of packets not within normal parameters:", temp);
Daniel Willmannafce8662018-07-06 23:11:32 +02001862 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001863 }
Philipp Maier36291392018-07-25 09:40:44 +02001864
1865 f_rtpem_stats_err_check(stats[0]);
1866 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001867
1868 /* Tear down */
1869 f_flow_delete(RTPEM[0]);
1870 f_flow_delete(RTPEM[1], ep, call_id);
1871 setverdict(pass);
1872 }
1873
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02001874 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1875 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1876 mp_local_ipv4, mp_remote_ipv4);
1877 }
1878
1879 testcase TC_two_crcx_mdcx_and_rtp_ipv6() runs on dummy_CT {
1880 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv6, mp_remote_ipv6,
1881 mp_local_ipv6, mp_remote_ipv6);
1882 }
1883
1884 testcase TC_two_crcx_mdcx_and_rtp_ipv4_ipv6() runs on dummy_CT {
1885 f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4,
1886 mp_local_ipv6, mp_remote_ipv6);
1887 }
1888
Philipp Maier2321ef92018-06-27 17:52:04 +02001889 /* Test what happens when two RTP streams from different sources target
1890 * a single connection. Is the unsolicited stream properly ignored? */
1891 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1892 var RtpFlowData flow[2];
1893 var RtpemStats stats[2];
1894 var MgcpResponse resp;
1895 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1896 var MgcpCallId call_id := '1234321326'H;
1897 var integer unsolicited_port := 10002;
1898
1899 f_init(ep);
1900
1901 /* from us to MGW */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001902 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001903 /* bind local RTP emulation sockets */
1904 flow[0].em.portnr := 10000;
1905 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1906
1907 /* from MGW back to us */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001908 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 98, "AMR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001909 flow[1].em.portnr := 20000;
1910 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001911
1912 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1913 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1914
Philipp Maier2321ef92018-06-27 17:52:04 +02001915 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001916
Philipp Maier2321ef92018-06-27 17:52:04 +02001917 /* Start inserting unsolicited RTP packets */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001918 f_rtpem_bind(RTPEM[2], mp_local_ipv4, unsolicited_port);
1919 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001920 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1921
1922 f_sleep(0.5);
1923
Daniel Willmanna069d382018-12-13 13:53:33 +01001924 /* Stop transmitting packets and tear down the flows */
1925 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02001926 f_flow_delete(RTPEM[0]);
1927 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001928
1929 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1930 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1931 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1932 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001933 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001934 }
1935
Philipp Maier36291392018-07-25 09:40:44 +02001936 f_rtpem_stats_err_check(stats[0]);
1937 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02001938
Harald Weltebb7523b2018-03-29 08:52:01 +02001939 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001940 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001941
Philipp Maier2321ef92018-06-27 17:52:04 +02001942 /* Test a handover situation. We first create two connections transmit
1943 * some data bidirectionally. Then we will simulate a handover situation. */
1944 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1945 var RtpFlowData flow[2];
1946 var RtpemStats stats[3];
1947 var MgcpResponse resp;
1948 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1949 var MgcpCallId call_id := '76338'H;
1950 var integer port_old;
1951
1952 f_init(ep);
1953
1954 /* First connection (BTS) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001955 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001956 /* bind local RTP emulation sockets */
1957 flow[0].em.portnr := 10000;
1958 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1959
1960 /* Second connection (PBX) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001961 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 110, "GSM-EFR/8000"));
Philipp Maier2321ef92018-06-27 17:52:04 +02001962 flow[1].em.portnr := 20000;
1963 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1964
1965 /* Normal rtp flow for one second */
1966 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1967 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1968 f_sleep(1.0);
1969
1970 /* Now switch the flow over to a new port (BTS) */
1971 port_old := flow[0].em.portnr;
1972 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001973 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02001974 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001975 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02001976
1977 /* When handing over a call, the old source may still keep
1978 * transmitting for a while. We simulate this by injecting
1979 * some unsolicited packets on the behalf of the old source,
1980 * (old remote port) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02001981 f_rtpem_bind(RTPEM[2], mp_local_ipv4, port_old);
1982 f_rtpem_connect(RTPEM[2], mp_remote_ipv4, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001983 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1984 f_sleep(1.0);
1985 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
1986 f_sleep(1.0);
1987
1988 /* Terminate call */
1989 f_flow_delete(RTPEM[0]);
1990 f_flow_delete(RTPEM[1], ep, call_id);
1991
1992 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1993 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1994 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
1995 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001996 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001997 }
1998 stats[2] := f_rtpem_stats_get(RTPEM[2]);
1999 if (stats[2].num_pkts_rx_err_disabled != 0) {
2000 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02002001 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02002002 }
2003
Philipp Maier36291392018-07-25 09:40:44 +02002004 f_rtpem_stats_err_check(stats[0]);
2005 f_rtpem_stats_err_check(stats[1]);
2006 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02002007
Philipp Maier2321ef92018-06-27 17:52:04 +02002008 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02002009 }
Harald Weltef53f1642017-11-18 19:57:11 +01002010
Philipp Maier6d4e0942019-02-21 17:35:01 +01002011
2012 /* create two local RTP emulations; create two connections on MGW EP, see if
2013 * exchanged data is converted bwtween ts101318 and rfc5993 */
2014 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
2015 var RtpFlowData flow[2];
2016 var RtpemStats stats[2];
2017 var MgcpResponse resp;
2018 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2019 var MgcpCallId call_id := '1226'H;
2020
2021 f_init(ep);
2022
2023 /* Turn on conversion mode */
2024 f_vty_enter_config(MGWVTY);
2025 f_vty_transceive(MGWVTY, "mgcp");
2026 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
2027
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002028 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002029 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002030 /* bind local RTP emulation sockets */
2031 flow[0].em.portnr := 10000;
2032 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2033 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2034 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2035 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
2036 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2037
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002038 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002039 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
Philipp Maier6d4e0942019-02-21 17:35:01 +01002040 flow[1].em.portnr := 20000;
2041 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2042 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2043 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2044 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
2045 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2046
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002047 /* Send RTP packets to connection #0, receive on connection #1 */
2048 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2049 f_sleep(0.5);
2050 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002051 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002052 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2053 f_sleep(0.5);
2054 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002055
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002056 /* Send RTP packets to connection #1, receive on connection #0 */
2057 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2058 f_sleep(0.5);
2059 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2060 f_sleep(1.0);
2061 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2062 f_sleep(0.5);
2063 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2064
2065 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002066 f_flow_delete(RTPEM[0]);
2067 f_flow_delete(RTPEM[1], ep, call_id);
2068
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002069 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002070 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2071 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002072 f_rtpem_stats_err_check(stats[0]);
2073 f_rtpem_stats_err_check(stats[1]);
2074
2075 /* Turn off conversion mode */
2076 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2077
2078 setverdict(pass);
2079 }
2080
Philipp Maier4f764ce2019-03-07 10:54:10 +01002081 /* create two local RTP emulations; create two connections on MGW EP, see if
2082 * exchanged data is converted between AMR octet-aligned and bandwith
2083 * efficient-mode */
2084 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2085 var RtpFlowData flow[2];
2086 var RtpemStats stats[2];
2087 var MgcpResponse resp;
2088 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2089 var MgcpCallId call_id := '1226'H;
2090
2091 f_init(ep);
2092
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002093 /* Connection #0 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002094 flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002095 /* bind local RTP emulation sockets */
2096 flow[0].em.portnr := 10000;
2097 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2098 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2099 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2100 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2101 flow[0].fmtp := fmtp0;
2102 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2103
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002104 /* Connection #1 (Bidirectional) */
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002105 flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
Philipp Maier4f764ce2019-03-07 10:54:10 +01002106 flow[1].em.portnr := 20000;
2107 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2108 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2109 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2110 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2111 flow[1].fmtp := fmtp1;
2112 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2113
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002114 /* Send RTP packets to connection #0, receive on connection #1 */
2115 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2116 f_sleep(0.5);
2117 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002118 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002119 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2120 f_sleep(0.5);
2121 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002122
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002123 /* Send RTP packets to connection #1, receive on connection #0 */
2124 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2125 f_sleep(0.5);
2126 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2127 f_sleep(1.0);
2128 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2129 f_sleep(0.5);
2130 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2131
2132 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002133 f_flow_delete(RTPEM[0]);
2134 f_flow_delete(RTPEM[1], ep, call_id);
2135
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002136 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002137 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2138 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002139 f_rtpem_stats_err_check(stats[0]);
2140 f_rtpem_stats_err_check(stats[1]);
2141
2142 setverdict(pass);
2143 }
2144
Philipp Maier882843d2020-05-25 15:33:13 +02002145 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2146 * functions are real world AMR RTP payloads including AMR header. The
2147 * payloads were extracted from a trace with known good payloads. */
2148
Philipp Maier4f764ce2019-03-07 10:54:10 +01002149 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002150 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002151 }
2152
2153 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2154 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2155 }
2156
2157 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2158 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2159 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002160
Harald Weltee636afd2017-09-17 16:24:09 +08002161 /* TODO: Double-DLCX (no retransmission) */
2162
2163
2164
2165 /* TODO: AUEP (various) */
2166 /* TODO: RSIP (various) */
2167 /* TODO: RQNT (various) */
2168 /* TODO: EPCF (various) */
2169 /* TODO: AUCX (various) */
2170 /* TODO: invalid verb (various) */
2171
Oliver Smith021141e2019-06-25 12:09:01 +02002172
2173 testcase TC_conn_timeout() runs on dummy_CT {
2174 var RtpFlowData flow;
2175 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2176 var MgcpCallId call_id := '1225'H;
2177 var MGCP_RecvFrom mrf;
2178
2179 f_init(ep);
2180 log("Setting conn-timeout to 1s");
2181 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2182
2183 log("Sending RTP data for 1.5s");
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002184 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Oliver Smith021141e2019-06-25 12:09:01 +02002185 flow.em.portnr := 10000;
2186 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2187 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2188 f_sleep(1.5);
2189
2190 log("Stopping for 0.5s and resuming");
2191 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2192 f_sleep(0.5);
2193 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2194 f_sleep(0.1);
2195
2196 log("Stopping for 1.5s, expecting to run into timeout");
2197 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2198 f_sleep(1.5);
2199
2200 log("Resuming should fail now");
2201 f_rtpem_conn_refuse_expect(RTPEM[0]);
2202 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2203 f_sleep(0.2);
2204 f_rtpem_conn_refuse_verify(RTPEM[0]);
2205
2206 setverdict(pass);
2207 }
2208
Philipp Maier2609c752020-07-08 12:38:09 +02002209 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2210 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2211 var template MgcpCommand cmd;
2212 var MgcpResponse resp;
2213 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2214 var MgcpCallId call_id := '8376F297'H;
2215
2216 f_init(ep);
2217
2218 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2219 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2220
2221 f_dlcx_ok(ep);
2222
2223 setverdict(pass);
2224 }
2225
2226 /* Test what happens when overlapping endpoints are selected (E1) */
2227 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2228 var template MgcpCommand cmd;
2229 var MgcpResponse resp;
2230 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2231 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2232 var MgcpCallId call_id_1 := '8376F297'H;
2233 var MgcpCallId call_id_2 := '837AF2A7'H;
2234
2235 f_init();
2236
2237 /* ep_1 and ep_2 are overlapping, selecting both one after
2238 * another should work fine: */
2239 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2240 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2241 f_dlcx_ok(ep_1);
2242 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2243 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2244 f_dlcx_ok(ep_2);
2245
2246 /* When ep_1 is serving a call we can not select ep_2 becaus
2247 * it is overlapping with ep_1 */
2248 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2249 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2250 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2251 resp := mgcp_transceive_mgw(cmd, ?);
2252 if (resp.line.code != "501") {
2253 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2254 }
2255 f_dlcx_ok(ep_1);
2256
2257 setverdict(pass);
2258 }
2259
2260 /* Create one connection in loopback mode, test if the RTP packets are
2261 * actually reflected */
2262 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2263 var RtpFlowData flow;
2264 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2265 var MgcpCallId call_id := '12250989'H;
2266 var RtpemStats stats;
2267
2268 f_init(ep);
Pau Espin Pedrolb604af02020-09-07 17:12:39 +02002269 flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1"));
Philipp Maier2609c752020-07-08 12:38:09 +02002270 flow.em.portnr := 10000;
2271 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2272
2273 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2274 f_sleep(1.0);
2275 f_flow_delete(RTPEM[0], ep, call_id);
2276
2277 stats := f_rtpem_stats_get(RTPEM[0]);
2278
2279 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2280 setverdict(fail);
2281 }
2282 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2283 setverdict(fail);
2284 }
2285
2286 f_rtpem_stats_err_check(stats);
2287
2288 setverdict(pass);
2289 }
2290
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002291 /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */
2292 testcase TC_crcx_mdcx_ip4() runs on dummy_CT {
2293 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2294 var template MgcpCommand cmd;
2295 var MgcpResponse resp;
2296 var MgcpCallId call_id := '1234'H;
2297 var MgcpConnectionId conn_id;
2298
2299 f_init(ep);
2300
2301 /* create the connection on the MGW */
2302 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2303 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2304 conn_id := extract_conn_id(resp);
2305
2306 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2307 cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "98" },
2308 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2309 valueof(ts_SDP_ptime(20)) });
2310 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2311
2312 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2313 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2314 }
2315 if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) {
2316 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2317 }
2318
2319 /* clean-up */
2320 f_dlcx_ok(ep, call_id);
2321 setverdict(pass);
2322 }
2323
2324 /* test valid CRCX then MDCX with IPv6 address, MGW provides a local IPv6 too */
2325 testcase TC_crcx_mdcx_ip6() runs on dummy_CT {
2326 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2327 var template MgcpCommand cmd;
2328 var MgcpResponse resp;
2329 var MgcpCallId call_id := '1234'H;
2330 var MgcpConnectionId conn_id;
2331
2332 f_init(ep);
2333
2334 /* create the connection on the MGW */
2335 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2336 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2337 conn_id := extract_conn_id(resp);
2338
2339 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id);
2340 cmd.sdp := ts_SDP("::2", "::1", "23", "42", 2344, { "98" },
2341 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
2342 valueof(ts_SDP_ptime(20)) });
2343 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
2344
2345 if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) {
2346 setverdict(fail, "No RemoteConnection info found in MDCX ACK!");
2347 }
2348 if (not match(resp.sdp.connection, ts_SDP_connection_IP("::1", "IP6"))) {
2349 setverdict(fail, "Wrong RemoteConnection in MDCX ACK!", resp.sdp.connection);
2350 }
2351
2352 /* clean-up */
2353 f_dlcx_ok(ep, call_id);
2354 setverdict(pass);
2355 }
2356
Harald Welte00a067f2017-09-13 23:27:17 +02002357 control {
2358 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002359 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002360 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002361 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002362 execute(TC_crcx_unsupp_mode());
2363 execute(TC_crcx_early_bidir_mode());
2364 execute(TC_crcx_unsupp_param());
2365 execute(TC_crcx_missing_callid());
2366 execute(TC_crcx_missing_mode());
2367 execute(TC_crcx_unsupp_packet_intv());
2368 execute(TC_crcx_illegal_double_lco());
2369 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002370 execute(TC_crcx_wildcarded());
2371 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002372 execute(TC_mdcx_without_crcx());
2373 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002374 execute(TC_mdcx_wildcarded());
2375 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002376 execute(TC_crcx_and_dlcx_ep_callid_connid());
2377 execute(TC_crcx_and_dlcx_ep_callid());
2378 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002379 execute(TC_crcx_and_dlcx_ep_callid_inval());
2380 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002381 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002382
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002383 execute(TC_crcx_osmux_wildcard());
2384 execute(TC_crcx_osmux_fixed());
2385 execute(TC_crcx_osmux_fixed_twice());
2386 execute(TC_one_crcx_receive_only_osmux());
2387 execute(TC_one_crcx_loopback_osmux());
2388 execute(TC_two_crcx_and_rtp_osmux());
2389 execute(TC_two_crcx_and_rtp_osmux_bidir());
2390 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2391 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2392
Harald Welte33d82162017-12-28 03:21:57 +01002393 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002394
2395 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002396
2397 execute(TC_one_crcx_receive_only_rtp());
2398 execute(TC_one_crcx_loopback_rtp());
Harald Weltebb7523b2018-03-29 08:52:01 +02002399 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002400 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002401 execute(TC_two_crcx_diff_pt_and_rtp());
2402 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002403 execute(TC_two_crcx_mdcx_and_rtp());
2404 execute(TC_two_crcx_and_unsolicited_rtp());
2405 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002406 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002407 execute(TC_amr_oa_bwe_rtp_conversion());
2408 execute(TC_amr_oa_oa_rtp_conversion());
2409 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002410
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002411 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002412
2413 execute(TC_e1_crcx_and_dlcx_ep());
2414 execute(TC_e1_crcx_with_overlap());
2415 execute(TC_e1_crcx_loopback());
2416
Pau Espin Pedroldb2dc042020-09-07 16:30:29 +02002417 execute(TC_crcx_mdcx_ip4());
2418 execute(TC_crcx_mdcx_ip6());
Pau Espin Pedrol9e0141a2020-09-07 17:25:56 +02002419 execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6());
2420 execute(TC_two_crcx_mdcx_and_rtp_ipv6());
2421 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6());
2422 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6());
2423 execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4());
2424 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6());
2425 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6());
2426 execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4());
Harald Welte00a067f2017-09-13 23:27:17 +02002427 }
2428}