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