blob: aeb6ef8629f493ccd25ae9aaa59c15a2c0e27e4b [file] [log] [blame]
Harald Welte34b5a952019-05-27 11:54:11 +02001/* MGW (Media Gateway) test suite in TTCN-3
2 * (C) 2017-2018 Harald Welte <laforge@gnumonks.org>
3 * (C) 2018-2019 sysmocom - s.f.m.c. GmbH
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
Harald Welte00a067f2017-09-13 23:27:17 +020012module MGCP_Test {
Harald Weltef07c2862017-11-18 17:16:24 +010013 import from Osmocom_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +020014 import from MGCP_Types all;
Harald Welte4029e8c2017-11-23 22:00:42 +010015 import from MGCP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080016 import from SDP_Types all;
17 import from MGCP_CodecPort all;
18 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +010019 import from RTP_CodecPort all;
20 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020021 import from RTP_Emulation all;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020022 import from OSMUX_Types all;
23 import from OSMUX_CodecPort all;
24 import from OSMUX_CodecPort_CtrlFunct all;
25 import from OSMUX_Emulation all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080026 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010027 import from General_Types all;
28 import from Native_Functions all;
29 import from IPCP_Types all;
30 import from IP_Types all;
31 import from Osmocom_VTY_Functions all;
32 import from TELNETasp_PortType all;
Harald Welte00a067f2017-09-13 23:27:17 +020033
Philipp Maierbb7a01c2018-02-01 12:32:57 +010034 const charstring c_mgw_domain := "mgw";
35 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
36
Harald Welte21ba5572017-09-19 17:55:05 +080037 /* any variables declared in the component will be available to
38 * all functions that 'run on' the named component, similar to
39 * class members in C++ */
Harald Welte00a067f2017-09-13 23:27:17 +020040 type component dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080041 port MGCP_CODEC_PT MGCP;
42 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010043 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080044 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010045
Philipp Maier2321ef92018-06-27 17:52:04 +020046 var RTP_Emulation_CT vc_RTPEM[3];
47 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010048
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020049 var OSMUX_Emulation_CT vc_OsmuxEM;
50 port OsmuxEM_CTRL_PT OsmuxEM;
51
Philipp Maier6137c322019-02-20 16:13:41 +010052 port TELNETasp_PT MGWVTY;
Harald Welte00a067f2017-09-13 23:27:17 +020053 };
54
Harald Weltee1e18c52017-09-17 16:23:07 +080055 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
56 var MgcpTransId tid := int2str(g_trans_id);
57 g_trans_id := g_trans_id + 1;
58 return tid;
59 }
60
Harald Welte21ba5572017-09-19 17:55:05 +080061 /* all parameters declared here can be modified / overridden by
62 * the config file in the [MODULE_PARAMETERS] section. If no
63 * config file is used or the file doesn't specify them, the
64 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080065 modulepar {
66 PortNumber mp_local_udp_port := 2727;
67 charstring mp_local_ip := "127.0.0.1";
68 PortNumber mp_remote_udp_port := 2427;
69 charstring mp_remote_ip := "127.0.0.1";
Harald Weltef07c2862017-11-18 17:16:24 +010070 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020071 PortNumber mp_local_osmux_port := 1985;
Harald Welte3c6ebb92017-09-16 00:56:57 +080072 }
73
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020074 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
75 /* Turn on conversion mode */
76 f_vty_enter_config(MGWVTY);
77 f_vty_transceive(MGWVTY, "mgcp");
78 if (osmux_on) {
79 f_vty_transceive(MGWVTY, "osmux on");
80 } else {
81 f_vty_transceive(MGWVTY, "osmux off");
82 }
83 f_vty_transceive(MGWVTY, "exit");
84 f_vty_transceive(MGWVTY, "exit");
85
86 }
87
88 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +010089 map(self:MGWVTY, system:MGWVTY);
90 f_vty_set_prompts(MGWVTY);
91 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020092
93 f_vty_enable_osmux(osmux_on);
Oliver Smith021141e2019-06-25 12:09:01 +020094 f_vty_config(MGWVTY, "mgcp", "conn-timeout 0");
Philipp Maier6137c322019-02-20 16:13:41 +010095 }
96
Harald Weltebb7523b2018-03-29 08:52:01 +020097 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
98 runs on dummy_CT {
99 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
100 map(comp_ref:RTP, system:RTP);
101 map(comp_ref:RTCP, system:RTCP);
102 comp_ref.start(RTP_Emulation.f_main());
103 }
104
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200105 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
106 runs on dummy_CT {
107 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
108 map(comp_ref:OSMUX, system:OSMUX);
109 comp_ref.start(OSMUX_Emulation.f_main());
110 }
111
Harald Welte21ba5572017-09-19 17:55:05 +0800112 /* initialization function, called by each test case at the
113 * beginning, but 'initialized' variable ensures its body is
114 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200115 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800116 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100117 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200118
Harald Welteedc45c12017-11-18 19:15:05 +0100119 if (initialized == false) {
120 initialized := true;
121
122 /* some random number for the initial transaction id */
123 g_trans_id := float2int(rnd()*65535.0);
124 map(self:MGCP, system:MGCP_CODEC_PT);
125 /* connect the MGCP test port using the given
126 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
127 * */
128 res := MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, mp_remote_ip, mp_remote_udp_port, mp_local_ip, mp_local_udp_port, 0, { udp := {} });
Harald Welte9220f632018-05-23 20:27:02 +0200129 if (not ispresent(res.connId)) {
130 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200131 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200132 }
Harald Welteedc45c12017-11-18 19:15:05 +0100133 g_mgcp_conn_id := res.connId;
134
Harald Weltebb7523b2018-03-29 08:52:01 +0200135 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
136 f_rtpem_init(vc_RTPEM[i], i);
137 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
138 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200139 if (osmux_on) {
140 f_osmuxem_init(vc_OsmuxEM);
141 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
142 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800143 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800144
Harald Welteedc45c12017-11-18 19:15:05 +0100145 if (isvalue(ep)) {
146 /* do a DLCX on all connections of the EP */
147 f_dlcx_ignore(valueof(ep));
148 }
Philipp Maier6137c322019-02-20 16:13:41 +0100149
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200150 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800151 }
152
Harald Welte00a067f2017-09-13 23:27:17 +0200153 testcase TC_selftest() runs on dummy_CT {
154 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 +0100155 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 +0200156 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
157 "I: 1\n" &
158 "\n" &
159 "v=0\r\n" &
160 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
161 "s=-\r\n" &
162 "c=IN IP4 0.0.0.0\r\n" &
163 "t=0 0\r\n" &
164 "m=audio 0 RTP/AVP 126\r\n" &
165 "a=rtpmap:126 AMR/8000\r\n" &
166 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100167 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 +0200168 "M: sendrecv\r" &
169 "C: 2\r\n" &
170 "I: 1\r\n" &
171 "L: p:20, a:AMR, nt:IN\r\n" &
172 "\n" &
173 "v=0\r\n" &
174 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800175 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200176 "c=IN IP4 0.0.0.0\r\n" &
177 "t=0 0\r\n" &
178 "m=audio 4441 RTP/AVP 99\r\n" &
179 "a=rtpmap:99 AMR/8000\r\n" &
180 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800181 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200182
183 log(c_auep);
184 log(dec_MgcpCommand(c_auep));
185
186 log(c_mdcx3);
187 log(dec_MgcpCommand(c_mdcx3));
188
189 log(c_mdcx3_ret);
190 log(dec_MgcpResponse(c_mdcx3_ret));
191
192 log(c_mdcx4);
193 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800194
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100195 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
196 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 +0800197
198 log(c_crcx510_ret);
199 log(dec_MgcpResponse(c_crcx510_ret));
200 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100201
202 /* We didn't encounter any DTE, so pass the test */
203 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800204 }
205
Harald Weltee636afd2017-09-17 16:24:09 +0800206 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100207 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800208 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
209 * - CRCX with remote session description and without
210 *
211 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100212 * x packetization != 20ms
213 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800214 * x unsupported mode (517)
215 * x bidirectional mode before RemoteConnDesc: 527
216 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100217 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800218 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
219 */
220
Harald Welte21ba5572017-09-19 17:55:05 +0800221 /* build a receive template for receiving a MGCP message. You
222 * pass the MGCP response template in, and it will generate an
223 * MGCP_RecvFrom template that can match the primitives arriving on the
224 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800225 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
226 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100227 connId := g_mgcp_conn_id,
Harald Weltee636afd2017-09-17 16:24:09 +0800228 remName := mp_remote_ip,
229 remPort := mp_remote_udp_port,
230 locName := mp_local_ip,
231 locPort := mp_local_udp_port,
232 msg := { response := resp }
233 }
234 return mrf;
235 }
236
237 /* Send a MGCP request + receive a (matching!) response */
238 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
239 var MgcpMessage msg := { command := valueof(cmd) };
240 resp.line.trans_id := cmd.line.trans_id;
241 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800242 var MGCP_RecvFrom mrf;
243 timer T := 5.0;
244
Harald Welte55015362017-11-18 16:02:42 +0100245 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800246 T.start;
247 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800248 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200249 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
250 setverdict(fail, "Response didn't match template");
251 mtc.stop;
252 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800253 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200254 [] T.timeout {
255 setverdict(fail, "Timeout waiting for response to ", cmd);
256 mtc.stop;
257 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800258 }
259 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800260
261 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
262 return mrf.msg.response;
263 } else {
264 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
265 return r;
266 }
Harald Welte00a067f2017-09-13 23:27:17 +0200267 }
268
Harald Welteba62c8c2017-11-18 18:26:49 +0100269 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
270 var integer i;
271 for (i := 0; i < lengthof(resp.params); i := i + 1) {
272 var MgcpParameter par := resp.params[i];
273 if (par.code == "I") {
274 return str2hex(par.val);
275 }
276 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200277 setverdict(fail, "Could not find conn id for MgcpReponse");
278 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100279 return '00000000'H;
280 }
281
Harald Welte10889c12017-11-18 19:40:31 +0100282 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
283 template MgcpCallId call_id := omit,
284 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100285 var template MgcpCommand cmd;
286 var MgcpResponse resp;
287 var template MgcpResponse rtmpl := {
288 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100289 code := ret_code,
290 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100291 },
292 params := *,
293 sdp := *
294 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100295 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100296 resp := mgcp_transceive_mgw(cmd, rtmpl);
297 }
298
Harald Welte10889c12017-11-18 19:40:31 +0100299 /* Send DLCX and expect OK response */
300 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
301 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100302 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100303 }
304
Harald Welteba62c8c2017-11-18 18:26:49 +0100305 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100306 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100307 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100308 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100309 }
310
Harald Weltebb7523b2018-03-29 08:52:01 +0200311 type record HostPort {
312 charstring hostname,
313 integer portnr optional
314 }
315 type record RtpFlowData {
316 HostPort em, /* emulation side */
317 HostPort mgw, /* mgw side */
318 uint7_t pt,
319 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200320 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100321 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200322 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
323 MgcpOsmuxCID osmux_cid optional,
324 MgcpOsmuxCID osmux_cid_response optional,
325 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100326 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200327 }
328
Philipp Maier2321ef92018-06-27 17:52:04 +0200329 /* Create an RTP flow (bidirectional, or receive-only) */
330 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 +0200331 boolean one_phase := true)
332 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200333 var template MgcpCommand cmd;
334 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100335 var SDP_attribute_list attributes;
336
337 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
338 if (isvalue(flow.fmtp)) {
339 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
340 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200341
342 /* bind local RTP emulation socket */
343 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
344
Philipp Maier28bb8292018-07-20 17:09:17 +0200345 /* configure rtp-emulation */
346 if (ispresent(flow.rtp_cfg)) {
347 f_rtpem_configure(pt, flow.rtp_cfg);
348 } else {
349 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
350 rtp_cfg.tx_payload_type := flow.pt
351 f_rtpem_configure(pt, rtp_cfg);
352 }
353
Harald Weltebb7523b2018-03-29 08:52:01 +0200354 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200355 /* Connect flow to MGW using a CRCX that also contains an SDP
356 * part that tells the MGW where we are listening for RTP streams
357 * that come from the MGW. We get a fully working connection in
358 * one go. */
359
360 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200361 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100362 flow.em.portnr, { int2str(flow.pt) }, attributes);
363
Harald Weltebb7523b2018-03-29 08:52:01 +0200364 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
365 flow.mgcp_conn_id := extract_conn_id(resp);
366 /* extract port number from response */
367 flow.mgw.portnr :=
368 resp.sdp.media_list[0].media_field.ports.port_number;
369 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200370 /* Create a half-open connection only. We do not tell the MGW
371 * where it can send RTP streams to us. This means this
372 * connection will only be able to receive but can not send
373 * data back to us. In order to turn the connection in a fully
374 * bi-directional one, a separate MDCX is needed. */
375
376 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200377 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
378 flow.mgcp_conn_id := extract_conn_id(resp);
379 /* extract MGW-side port number from response */
380 flow.mgw.portnr :=
381 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200382 }
383 /* finally, connect the emulation-side RTP socket to the MGW */
384 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
385 }
386
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200387 /* Create an Osmux flow (bidirectional, or receive-only) */
388 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
389 boolean one_phase := true)
390 runs on dummy_CT {
391 var template MgcpCommand cmd;
392 var MgcpResponse resp;
393 var SDP_attribute_list attributes;
394 var OsmuxTxHandle tx_hdl;
395 var OsmuxRxHandle rx_hdl;
396 var charstring cid_response;
397 var OsmuxCID cid_resp_parsed
398
399 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
400 if (isvalue(flow.fmtp)) {
401 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
402 }
403
404 /* bind local Osmux emulation socket */
405 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
406
407 /* configure osmux-emulation */
408 if (ispresent(flow.osmux_cfg)) {
409 f_osmuxem_configure(pt, flow.osmux_cfg);
410 } else {
411 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
412 f_osmuxem_configure(pt, osmux_cfg);
413 flow.osmux_cfg := osmux_cfg
414 }
415
416 if (one_phase) {
417 /* Connect flow to MGW using a CRCX that also contains an SDP
418 * part that tells the MGW where we are listening for Osmux streams
419 * that come from the MGW. We get a fully working connection in
420 * one go. */
421 rx_hdl := c_OsmuxemDefaultRxHandle;
422 rx_hdl.cid := flow.osmux_cid;
423 f_osmuxem_register_rxhandle(pt, rx_hdl);
424 flow.osmux_cid_sent := true;
425 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
426 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
427 flow.em.portnr, { int2str(flow.pt) }, attributes);
428 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
429 flow.mgcp_conn_id := extract_conn_id(resp);
430 /* extract port number from response */
431 flow.mgw.portnr :=
432 resp.sdp.media_list[0].media_field.ports.port_number;
433 } else {
434 /* Create a half-open connection only. We do not tell the MGW
435 * where it can send Osmux streams to us. This means this
436 * connection will only be able to receive but can not send
437 * data back to us. In order to turn the connection in a fully
438 * bi-directional one, a separate MDCX is needed. */
439
440 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
441 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
442
443 flow.mgcp_conn_id := extract_conn_id(resp);
444 /* extract MGW-side port number from response */
445 flow.mgw.portnr :=
446 resp.sdp.media_list[0].media_field.ports.port_number;
447 }
448
449 /* extract Osmux CID we got assigned by the MGW */
450 var MgcpMessage resp_msg := {
451 response := resp
452 }
453
454 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
455 setverdict(fail, "No Osmux CID in MGCP response", resp);
456 mtc.stop;
457 }
458
459 /* Make sure response is no wildcard */
460 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
461 if (flow.osmux_cid_response == -1) {
462 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
463 mtc.stop;
464 }
465 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
466 f_osmuxem_register_txhandle(pt, tx_hdl);
467
468 /* finally, connect the emulation-side RTP socket to the MGW */
469 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
470 }
471
Philipp Maier2321ef92018-06-27 17:52:04 +0200472 /* Modify an existing RTP flow */
473 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
474 runs on dummy_CT {
475 var template MgcpCommand cmd;
476 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100477 var SDP_attribute_list attributes;
478
479 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
480 if (isvalue(flow.fmtp)) {
481 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
482 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200483
484 /* rebind local RTP emulation socket to the new address */
485 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
486
Philipp Maier28bb8292018-07-20 17:09:17 +0200487 /* reconfigure rtp-emulation */
488 if (ispresent(flow.rtp_cfg)) {
489 f_rtpem_configure(pt, flow.rtp_cfg);
490 } else {
491 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
492 rtp_cfg.tx_payload_type := flow.pt
493 f_rtpem_configure(pt, rtp_cfg);
494 }
495
Philipp Maier2321ef92018-06-27 17:52:04 +0200496 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
497 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
498 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100499 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200500 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
501
502 /* extract MGW-side port number from response. (usually this
503 * will not change, but thats is up to the MGW) */
504 flow.mgw.portnr :=
505 resp.sdp.media_list[0].media_field.ports.port_number;
506
507 /* reconnect the emulation-side RTP socket to the MGW */
508 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
509 }
510
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200511 /* Modify an existing Osmux flow */
512 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
513 runs on dummy_CT {
514 var template MgcpCommand cmd;
515 var MgcpResponse resp;
516 var SDP_attribute_list attributes;
517 var OsmuxRxHandle rx_hdl;
518 var charstring cid_response;
519 var OsmuxCID cid_resp_parsed
520
521 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
522 if (isvalue(flow.fmtp)) {
523 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
524 }
525
526 /* rebind local Osmux emulation socket to the new address */
527 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
528
529 /* configure osmux-emulation */
530 if (ispresent(flow.osmux_cfg)) {
531 f_osmuxem_configure(pt, flow.osmux_cfg);
532 } else {
533 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
534 f_osmuxem_configure(pt, osmux_cfg);
535 }
536
537 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
538 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
539 rx_hdl := c_OsmuxemDefaultRxHandle;
540 rx_hdl.cid := flow.osmux_cid;
541 f_osmuxem_register_rxhandle(pt, rx_hdl);
542 flow.osmux_cid_sent := true;
543 }
544
545 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
546 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
547 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
548 flow.em.portnr, { int2str(flow.pt) }, attributes);
549 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
550
551 /* extract MGW-side port number from response. (usually this
552 * will not change, but thats is up to the MGW) */
553 flow.mgw.portnr :=
554 resp.sdp.media_list[0].media_field.ports.port_number;
555
556 /* extract Osmux CID we got assigned by the MGW */
557 var MgcpMessage resp_msg := {
558 response := resp
559 }
560
561 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
562 setverdict(fail, "No Osmux CID in MGCP response", resp);
563 mtc.stop;
564 }
565
566 /* Make sure response is no wildcard */
567 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
568 if (cid_resp_parsed == -1) {
569 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
570 mtc.stop;
571 }
572 if (cid_resp_parsed != flow.osmux_cid_response) {
573 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
574 mtc.stop;
575 }
576
577 /* reconnect the emulation-side Osmux socket to the MGW */
578 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
579 }
580
Philipp Maier2321ef92018-06-27 17:52:04 +0200581 /* Delete an existing RTP flow */
582 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
583 runs on dummy_CT {
584 var template MgcpCommand cmd;
585 var MgcpResponse resp;
586
587 /* Switch off RTP flow */
588 f_rtpem_mode(pt, RTPEM_MODE_NONE);
589
590 /* Delete connection on MGW (if needed) */
591 if (isvalue(call_id) and isvalue(ep)) {
592 f_sleep(0.1);
593 f_dlcx_ok(valueof(ep), call_id);
594 }
595 }
596
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200597 /* Delete an existing Osmux flow */
598 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
599 runs on dummy_CT {
600 var template MgcpCommand cmd;
601 var MgcpResponse resp;
602
603 /* Switch off Osmux flow */
604 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
605
606 /* Delete connection on MGW (if needed) */
607 if (isvalue(call_id) and isvalue(ep)) {
608 f_sleep(0.1);
609 f_dlcx_ok(valueof(ep), call_id);
610 }
611 }
612
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100613 function f_crcx(charstring ep_prefix) runs on dummy_CT {
614 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800615 var template MgcpCommand cmd;
616 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100617 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800618
Harald Welteedc45c12017-11-18 19:15:05 +0100619 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800620
Harald Welteba62c8c2017-11-18 18:26:49 +0100621 /* create the connection on the MGW */
622 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100623 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100624 extract_conn_id(resp);
625
626 /* clean-up */
627 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100628 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100629
Philipp Maier45635f42018-06-05 17:28:02 +0200630 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
631 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
632 var template MgcpCommand cmd;
633 var MgcpResponse resp;
634 var MgcpCallId call_id := '1234'H;
635
636 f_init(ep);
637
638 /* create the connection on the MGW */
639 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
640 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
641 extract_conn_id(resp);
642
643 /* clean-up */
644 f_dlcx_ok(ep, call_id);
645
646 /* See also OS#2658: Even when we omit the LCO information, we
647 expect the MGW to pick a sane payload type for us. This
648 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200649 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200650 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200651 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200652 }
653
654 /* See also OS#2658: We also expect the MGW to assign a port
655 number to us. */
656 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
657 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200658 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200659 }
660 }
661
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200662 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
663 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
664 var template MgcpCommand cmd;
665 var MgcpResponse resp;
666 var MgcpCallId call_id := '1234'H;
667 var charstring cid_response;
668
669 if (run_init) {
670 f_init(ep, true);
671 }
672
673 /* create the connection on the MGW */
674 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
675 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
676 extract_conn_id(resp);
677
678 /* extract Osmux CID we got assigned by the MGW */
679 var MgcpMessage resp_msg := {
680 response := resp
681 }
682
683 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
684 setverdict(fail, "No Osmux CID in MGCP response", resp);
685 mtc.stop;
686 }
687
688 /* Make sure response is no wildcard */
689 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
690 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
691 mtc.stop;
692 }
693
694 /* clean-up */
695 f_dlcx_ok(ep, call_id);
696 }
697
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100698 /* test valid CRCX without SDP */
699 testcase TC_crcx() runs on dummy_CT {
700 f_crcx(c_mgw_ep_rtpbridge);
701 setverdict(pass);
702 }
703
Philipp Maier45635f42018-06-05 17:28:02 +0200704 /* test valid CRCX without SDP and LCO */
705 testcase TC_crcx_no_lco() runs on dummy_CT {
706 f_crcx_no_lco(c_mgw_ep_rtpbridge);
707 setverdict(pass);
708 }
709
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100710 /* test valid CRCX without SDP (older method without endpoint prefix) */
711 testcase TC_crcx_noprefix() runs on dummy_CT {
712 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800713 setverdict(pass);
714 }
715
716 /* test CRCX with unsupported mode, expect 517 */
717 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
718 var template MgcpCommand cmd;
719 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100720 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100721 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800722 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
723
Harald Welteedc45c12017-11-18 19:15:05 +0100724 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800725
Harald Welteba62c8c2017-11-18 18:26:49 +0100726 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800727 resp := mgcp_transceive_mgw(cmd, rtmpl);
728 setverdict(pass);
729 }
730
Harald Welte21ba5572017-09-19 17:55:05 +0800731 /* test CRCX with early bi-directional mode, expect 527 as
732 * bi-diretional media can only be established once both local and
733 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800734 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
735 var template MgcpCommand cmd;
736 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100737 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100738 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800739 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
740
Harald Welteedc45c12017-11-18 19:15:05 +0100741 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800742
Harald Welteba62c8c2017-11-18 18:26:49 +0100743 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800744 resp := mgcp_transceive_mgw(cmd, rtmpl);
745 setverdict(pass);
746 }
747
748 /* test CRCX with unsupported Parameters */
749 testcase TC_crcx_unsupp_param() runs on dummy_CT {
750 var template MgcpCommand cmd;
751 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100752 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100753 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800754 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
755
Harald Welteedc45c12017-11-18 19:15:05 +0100756 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800757
Harald Welteba62c8c2017-11-18 18:26:49 +0100758 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100759 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
760 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
761
Harald Weltee636afd2017-09-17 16:24:09 +0800762 resp := mgcp_transceive_mgw(cmd, rtmpl);
763 setverdict(pass);
764 }
765
766 /* test CRCX with missing CallId */
767 testcase TC_crcx_missing_callid() 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 Weltef91edf32017-12-28 14:16:21 +0100771 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800772
Harald Welteedc45c12017-11-18 19:15:05 +0100773 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800774
Harald Welteba62c8c2017-11-18 18:26:49 +0100775 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800776 cmd.params := {
777 t_MgcpParConnMode("recvonly"),
778 t_MgcpParLocConnOpt("p:20")
779 }
780 resp := mgcp_transceive_mgw(cmd, rtmpl);
781 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100782
Harald Weltee636afd2017-09-17 16:24:09 +0800783 }
784
785 /* test CRCX with missing Mode */
786 testcase TC_crcx_missing_mode() runs on dummy_CT {
787 var template MgcpCommand cmd;
788 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100789 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100790 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100791 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800792
Harald Welteedc45c12017-11-18 19:15:05 +0100793 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800794
Harald Welteba62c8c2017-11-18 18:26:49 +0100795 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800796 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100797 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800798 t_MgcpParLocConnOpt("p:20")
799 }
800 resp := mgcp_transceive_mgw(cmd, rtmpl);
801 setverdict(pass);
802 }
803
804 /* test CRCX with unsupported packetization interval */
805 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
806 var template MgcpCommand cmd;
807 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100808 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100809 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100810 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800811
Harald Welteedc45c12017-11-18 19:15:05 +0100812 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800813
Harald Welteba62c8c2017-11-18 18:26:49 +0100814 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100815 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800816 resp := mgcp_transceive_mgw(cmd, rtmpl);
817 setverdict(pass);
818 }
819
820 /* test CRCX with illegal double presence of local connection option */
821 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
822 var template MgcpCommand cmd;
823 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100824 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100825 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800826 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
827
Harald Welteedc45c12017-11-18 19:15:05 +0100828 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800829
Harald Welteba62c8c2017-11-18 18:26:49 +0100830 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100831 /* p:20 is permitted only once and not twice! */
832 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800833 resp := mgcp_transceive_mgw(cmd, rtmpl);
834 setverdict(pass);
835 }
836
837 /* test valid CRCX with valid SDP */
838 testcase TC_crcx_sdp() 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 := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800843
Harald Welteedc45c12017-11-18 19:15:05 +0100844 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800845
Harald Welteba62c8c2017-11-18 18:26:49 +0100846 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800847 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
848 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
849 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100850 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100851
852 /* clean-up */
853 f_dlcx_ok(ep, call_id);
854
Harald Weltee636afd2017-09-17 16:24:09 +0800855 setverdict(pass);
856 }
857
Philipp Maier5e06cee2018-02-01 18:28:08 +0100858 /* test valid wildcarded CRCX */
859 testcase TC_crcx_wildcarded() runs on dummy_CT {
860 var template MgcpCommand cmd;
861 var MgcpResponse resp;
862 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
863 var MgcpCallId call_id := '1234'H;
864 var MgcpEndpoint ep_assigned;
865 f_init();
866
867 /* create the connection on the MGW */
868 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
869 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
870 extract_conn_id(resp);
871
872 /* extract endpoint name we got assigned by the MGW */
873 var MgcpMessage resp_msg := {
874 response := resp
875 }
876 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
877 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200878 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100879 }
880
881 /* clean-up */
882 f_dlcx_ok(ep_assigned, call_id);
883
884 setverdict(pass);
885 }
886
887 /* test valid wildcarded CRCX */
888 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
889 const integer n_endpoints := 32;
890 var integer i;
891 var template MgcpCommand cmd;
892 var MgcpResponse resp;
893 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
894 var MgcpCallId call_id := '1234'H;
895 var MgcpEndpoint ep_assigned[n_endpoints];
896 f_init();
897
898 /* Exhaust all endpoint resources on the virtual trunk */
899 for (i := 0; i < n_endpoints; i := i+1) {
900 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
901 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
902
903 /* Make sure we got a connection id */
904 extract_conn_id(resp);
905
906 var MgcpMessage resp_msg := {
907 response := resp
908 }
909 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == 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
915 /* Try to allocate one more endpoint, which should fail */
916 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
917 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
918 resp := mgcp_transceive_mgw(cmd, rtmpl);
919 setverdict(pass);
920
921 /* clean-up */
922 for (i := 0; i < n_endpoints; i := i+1) {
923 f_dlcx_ok(ep_assigned[i], call_id);
924 }
925 setverdict(pass);
926 }
927
Harald Weltee636afd2017-09-17 16:24:09 +0800928 /* TODO: various SDP related bits */
929
930
931 /* TODO: CRCX with X-Osmux */
932 /* TODO: double CRCX without force_realloc */
933
934 /* TODO: MDCX (various) */
935
936 /* TODO: MDCX without CRCX first */
937 testcase TC_mdcx_without_crcx() runs on dummy_CT {
938 var template MgcpCommand cmd;
939 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100940 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100941 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800942 var template MgcpResponse rtmpl := {
943 line := {
944 /* TODO: accept/enforce better error? */
945 code := "400",
946 string := ?
947 },
948 params:= { },
949 sdp := omit
950 };
951
Harald Welteedc45c12017-11-18 19:15:05 +0100952 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800953
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100954 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800955 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
956 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
957 valueof(ts_SDP_ptime(20)) });
958 resp := mgcp_transceive_mgw(cmd, rtmpl);
959 setverdict(pass);
960 }
961
962 /* DLCX without CRCX first */
963 testcase TC_dlcx_without_crcx() runs on dummy_CT {
964 var template MgcpCommand cmd;
965 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100966 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800967 var template MgcpResponse rtmpl := {
968 line := {
Harald Weltef91edf32017-12-28 14:16:21 +0100969 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +0800970 string := ?
971 },
972 params:= { },
973 sdp := omit
974 };
975
Harald Welteedc45c12017-11-18 19:15:05 +0100976 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800977
Harald Welteedc45c12017-11-18 19:15:05 +0100978 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800979 resp := mgcp_transceive_mgw(cmd, rtmpl);
980 setverdict(pass);
981 }
982
Philipp Maier8a3dc922018-02-02 14:55:12 +0100983 /* test valid wildcarded MDCX */
984 testcase TC_mdcx_wildcarded() runs on dummy_CT {
985 /* Note: A wildcarded MDCX is not allowed, so we expect the
986 * MGW to reject this request */
987 var template MgcpCommand cmd;
988 var MgcpResponse resp;
989 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
990 var MgcpCallId call_id := '1225'H;
991 var template MgcpResponse rtmpl := {
992 line := {
993 /* TODO: accept/enforce better error? */
994 code := "507",
995 string := ?
996 },
997 params:= { },
998 sdp := omit
999 };
1000
1001 f_init(ep);
1002
1003 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1004 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1005 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1006 valueof(ts_SDP_ptime(20)) });
1007 resp := mgcp_transceive_mgw(cmd, rtmpl);
1008 setverdict(pass);
1009 }
1010
1011 /* test valid wildcarded DLCX */
1012 testcase TC_dlcx_wildcarded() runs on dummy_CT {
1013 /* Note: A wildcarded DLCX is specified, but our MGW does not
1014 * support this feature so we expect the MGW to reject the
1015 * request */
1016 var template MgcpCommand cmd;
1017 var MgcpResponse resp;
1018 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1019 var template MgcpResponse rtmpl := {
1020 line := {
1021 code := "507",
1022 string := ?
1023 },
1024 params:= { },
1025 sdp := omit
1026 };
1027
1028 f_init(ep);
1029
1030 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1031 resp := mgcp_transceive_mgw(cmd, rtmpl);
1032 setverdict(pass);
1033 }
1034
Harald Welte79181ff2017-11-18 19:26:11 +01001035 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1036 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1037 var template MgcpCommand cmd;
1038 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001039 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001040 var MgcpCallId call_id := '51234'H;
1041
1042 f_init(ep);
1043
1044 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1045 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1046
1047 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1048
1049 setverdict(pass);
1050 }
1051
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001052 /* test valid CRCX without SDP */
1053 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1054 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1055 setverdict(pass);
1056 }
1057
1058 /* test valid CRCX without SDP */
1059 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1060 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1061 setverdict(pass);
1062 }
1063
1064 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1065 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1066 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1067 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1068 setverdict(pass);
1069 }
1070
1071 /* Create one half open connection in receive-only mode. The MGW must accept
1072 * the packets but must not send any. */
1073 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1074 var RtpFlowData flow;
1075 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1076 var MgcpCallId call_id := '1225'H;
1077 var OsmuxemStats stats;
1078 var OsmuxTxHandle tx_hdl;
1079
1080 f_init(ep, true);
1081 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
1082 flow.em.portnr := mp_local_osmux_port;
1083 flow.osmux_cid := -1;
1084 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1085
1086 /* create a transmitter not yet known by MGW */
1087 tx_hdl := valueof(t_TxHandleAMR590(2));
1088 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1089
1090 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1091 f_sleep(1.0);
1092 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1093
1094 stats := f_osmuxem_stats_get(OsmuxEM);
1095
1096 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1097 setverdict(fail);
1098 }
1099 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1100 setverdict(fail);
1101 }
1102
1103 f_osmuxem_stats_err_check(stats);
1104
1105 setverdict(pass);
1106 }
1107
1108 /* Create one connection in loopback mode, test if the Osmux packets are
1109 * actually reflected */
1110 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1111 var RtpFlowData flow;
1112 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1113 var MgcpCallId call_id := '1225'H;
1114 var OsmuxemStats stats;
1115 var OsmuxTxHandle tx_hdl;
1116
1117 f_init(ep, true);
1118 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1119 flow.em.portnr := mp_local_osmux_port;
1120 flow.osmux_cid := 2;
1121 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1122
1123 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1124 f_sleep(1.0);
1125
1126 /* Switch off both Tx, wait to receive delayed frames from MGW */
1127 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1128 f_sleep(0.1);
1129 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1130
1131 stats := f_osmuxem_stats_get(OsmuxEM);
1132
1133 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1134 setverdict(fail);
1135 }
1136 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1137 setverdict(fail);
1138 }
1139
1140 f_osmuxem_stats_err_check(stats);
1141
1142 setverdict(pass);
1143 }
1144
1145 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1146 * must match the reception statistics on the other side and vice versa. The
1147 * user may also supply a tolerance value (number of packets) when deviations
1148 * are acceptable */
1149 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1150 var integer plen;
1151
1152 log("stats A: ", a);
1153 log("stats B: ", b);
1154 log("tolerance: ", tolerance, " packets");
1155 log("batch_size: ", batch_size, " packets");
1156
1157 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1158
1159 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1160 return false;
1161 }
1162
1163 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1164 return false;
1165 }
1166
1167 if(a.num_pkts_tx > 0) {
1168 plen := a.bytes_payload_tx / a.num_pkts_tx;
1169 } else {
1170 plen := 0;
1171 }
1172
1173 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1174 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1175 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1176 return false;
1177 }
1178
1179 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) {
1180 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1181 return false;
1182 }
1183
1184 return true;
1185 }
1186
1187 function f_TC_two_crcx_and_rtp_osmux(boolean bidir) runs on dummy_CT {
1188 var RtpFlowData flow[2];
1189 var RtpemStats stats_rtp;
1190 var OsmuxemStats stats_osmux;
1191 var MgcpResponse resp;
1192 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1193 var MgcpCallId call_id := '1226'H;
1194 var integer tolerance := 0;
1195
1196 f_init(ep, true);
1197
1198 /* from us to MGW */
1199 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1200 flow[0].rtp_cfg := c_RtpemDefaultCfg
1201 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1202 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1203 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);
1204 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1205 /* bind local RTP emulation sockets */
1206 flow[0].em.portnr := 10000;
1207 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1208
1209 /* from MGW back to us */
1210 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "AMR/8000"));
1211 flow[1].em.portnr := mp_local_osmux_port;
1212 flow[1].osmux_cid := 2;
1213 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1214 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1215
1216 if (bidir) {
1217 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1218 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1219
1220 /* Note: When we test bidirectional we may
1221 * loose packets during switch off because
1222 * both ends are transmitting and we only
1223 * can switch them off one by one. */
1224 tolerance := 3;
1225 } else {
1226 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1227 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1228 }
1229
1230 f_sleep(1.0);
1231
1232 /* Switch off both Tx, wait to receive delayed frames from MGW */
1233 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1234 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1235 f_sleep(0.1);
1236
1237 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1238 f_flow_delete(RTPEM[1]);
1239
1240 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1241 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1242 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1243 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1244 mtc.stop;
1245 }
1246
1247 f_rtpem_stats_err_check(stats_rtp);
1248 f_osmuxem_stats_err_check(stats_osmux);
1249
1250 setverdict(pass);
1251 }
1252
1253 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1254 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
1255 f_TC_two_crcx_and_rtp_osmux(false);
1256 }
1257
1258 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1259 * exchange some data in both directions */
1260 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
1261 f_TC_two_crcx_and_rtp_osmux(true);
1262 }
1263
1264
1265 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard) runs on dummy_CT {
1266 var RtpFlowData flow[2];
1267 var RtpemStats stats_rtp;
1268 var OsmuxemStats stats_osmux;
1269 var MgcpResponse resp;
1270 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1271 var MgcpCallId call_id := '1227'H;
1272 var integer num_pkts_tx[2];
1273 var integer temp;
1274
1275 f_init(ep, true);
1276
1277 /* Create the first connection in receive only mode */
1278 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1279 flow[0].rtp_cfg := c_RtpemDefaultCfg
1280 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1281 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1282 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);
1283 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1284 /* bind local RTP emulation sockets */
1285 flow[0].em.portnr := 10000;
1286 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1287
1288
1289 /* Create the second connection. This connection will be also
1290 * in receive only mode */
1291 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "AMR/8000"));
1292 flow[1].em.portnr := mp_local_osmux_port;
1293 if (crcx_osmux_wildcard) {
1294 flow[1].osmux_cid := -1;
1295 } else {
1296 flow[1].osmux_cid := 2;
1297 }
1298 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1299 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], false);
1300
1301
1302 /* The first leg starts transmitting */
1303 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1304 f_sleep(0.5);
1305 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1306 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1307 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1308 mtc.stop;
1309 }
1310 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1311 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1312 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1313 mtc.stop;
1314 }
1315
1316 /* The second leg starts transmitting a little later */
1317 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1318 f_sleep(1.0);
1319 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1320 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1321 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1322 mtc.stop;
1323 }
1324 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1325 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1326 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1327 mtc.stop;
1328 }
1329
1330 /* The first leg will now be switched into bidirectional
1331 * mode, but we do not expect any data comming back yet. */
1332 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1333 f_sleep(0.5);
1334 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1335 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1336 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1337 mtc.stop;
1338 }
1339 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1340 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1341 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1342 mtc.stop;
1343 }
1344
1345 /* When the second leg is switched into bidirectional mode
1346 * as well, then the MGW will connect the two together and
1347 * we should see RTP streams passing through from both ends. */
1348 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1349 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1350 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1351 num_pkts_tx[0] := stats_rtp.num_pkts_tx
1352 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1353 num_pkts_tx[1] := stats_osmux.num_pkts_tx
1354
1355 if (crcx_osmux_wildcard) {
1356 /* For now we must set same CID as the MGW recvCID,
1357 * having sendCID!=recvCID is not yet supported. */
1358 flow[1].osmux_cid := flow[1].osmux_cid_response;
1359 }
1360 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1361 f_sleep(2.0);
1362
1363 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1364 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1365
1366 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1367 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1368 log("stats_rtp: ", stats_rtp);
1369 log("stats_osmux: ", stats_osmux);
1370 log("old_rtp_tx: ", num_pkts_tx[0]);
1371 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1372 mtc.stop;
1373 }
1374
1375 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1376 if (temp > 3 or temp < -3) {
1377 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1378 mtc.stop;
1379 }
1380
1381 f_rtpem_stats_err_check(stats_rtp);
1382 f_osmuxem_stats_err_check(stats_osmux);
1383
1384 /* Tear down */
1385 f_flow_delete(RTPEM[0]);
1386 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1387 setverdict(pass);
1388 }
1389
1390 /* create one RTP and one OSmux emulations and pass data in both
1391 directions. Create CRCX with wildcard Osmux CID and set it later
1392 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1393 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
1394 f_two_crcx_mdcx_and_rtp_osmux(true);
1395 }
1396
1397 /* create one RTP and one OSmux emulations and pass data in both
1398 directions. Create CRCX with fixed Osmux CID and keep it during
1399 MDCX. This is similar to how BSC sets up the call in AoIP. */
1400 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
1401 f_two_crcx_mdcx_and_rtp_osmux(false);
1402 }
1403
Harald Welte646ecdb2017-12-28 03:21:57 +01001404 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1405 var template MgcpCommand cmd;
1406 var MgcpResponse resp;
1407
1408 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1409 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1410
1411 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1412
1413 setverdict(pass);
1414 }
1415
1416 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1417 var MgcpEndpoint ep;
1418 var MgcpCallId call_id;
1419 var integer ep_nr;
1420
1421 f_init();
1422
1423 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001424 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
Harald Welte646ecdb2017-12-28 03:21:57 +01001425 call_id := int2hex(ep_nr, 2) & '1234'H;
1426 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1427 }
1428 }
1429
Harald Welte79181ff2017-11-18 19:26:11 +01001430 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1431 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001432 var template MgcpCommand cmd;
1433 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001434 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001435 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001436
Harald Welteedc45c12017-11-18 19:15:05 +01001437 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001438
Harald Welteba62c8c2017-11-18 18:26:49 +01001439 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001440 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001441
Harald Welteba62c8c2017-11-18 18:26:49 +01001442 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001443
1444 setverdict(pass);
1445 }
1446
Harald Welte79181ff2017-11-18 19:26:11 +01001447 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1448 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1449 var template MgcpCommand cmd;
1450 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001451 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001452 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001453
1454 f_init(ep);
1455
1456 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1457 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1458
1459 f_dlcx_ok(ep);
1460
1461 setverdict(pass);
1462 }
1463
1464
Harald Welte6d167f82017-11-18 19:41:35 +01001465 /* CRCX + DLCX of valid endpoint but invalid call-id */
1466 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1467 var template MgcpCommand cmd;
1468 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001469 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001470 var MgcpCallId call_id := '51231'H;
1471
1472 f_init(ep);
1473
1474 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1475 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1476
1477 f_dlcx(ep, "516", *, 'ffff'H);
1478
1479 setverdict(pass);
1480 }
1481
1482
1483 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1484 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1485 var template MgcpCommand cmd;
1486 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001487 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001488 var MgcpCallId call_id := '51230'H;
1489
1490 f_init(ep);
1491
1492 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1493 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1494
1495 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1496
1497 setverdict(pass);
1498 }
1499
1500
Harald Weltee636afd2017-09-17 16:24:09 +08001501 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001502 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1503 var template MgcpCommand cmd;
1504 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001505 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001506 var MgcpCallId call_id := '51229'H;
1507 var template MgcpResponse rtmpl := {
1508 line := {
1509 code := "200",
1510 string := "OK"
1511 },
1512 params:= { },
1513 sdp := omit
1514 };
1515
1516 f_init(ep);
1517
1518 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1519 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1520
1521 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1522 resp := mgcp_transceive_mgw(cmd, rtmpl);
1523 resp := mgcp_transceive_mgw(cmd, rtmpl);
1524
1525 setverdict(pass);
1526 }
1527
Harald Weltebb7523b2018-03-29 08:52:01 +02001528 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1529 charstring codec) := {
1530 em := {
1531 hostname := host_a,
1532 portnr := omit
1533 },
1534 mgw := {
1535 hostname := host_b,
1536 portnr := omit
1537 },
1538 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001539 codec := codec,
1540 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001541 }
Harald Weltef53f1642017-11-18 19:57:11 +01001542
Harald Weltebb7523b2018-03-29 08:52:01 +02001543 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1544 testcase TC_rtpem_selftest() runs on dummy_CT {
1545 var RtpemStats stats[2];
1546 var integer local_port := 10000;
1547 var integer local_port2 := 20000;
1548
1549 f_init();
1550
1551 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1552 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1553
1554 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1555 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1556
1557 log("=== starting");
1558 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1559 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1560
1561 f_sleep(5.0);
1562
1563 log("=== stopping");
1564 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1565 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1566 f_sleep(0.5);
1567 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1568 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1569
1570 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1571 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1572 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1573 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001574 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001575 }
1576 setverdict(pass);
1577 }
1578
Philipp Maier2321ef92018-06-27 17:52:04 +02001579 /* Create one half open connection in receive-only mode. The MGW must accept
1580 * the packets but must not send any. */
1581 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1582 var RtpFlowData flow;
1583 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1584 var MgcpCallId call_id := '1225'H;
1585 var RtpemStats stats;
1586
1587 f_init(ep);
1588 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
1589 flow.em.portnr := 10000;
1590 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, false);
1591
1592 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1593 f_sleep(1.0);
1594 f_flow_delete(RTPEM[0], ep, call_id);
1595
1596 stats := f_rtpem_stats_get(RTPEM[0]);
1597
1598 if (stats.num_pkts_tx < 40) {
1599 setverdict(fail);
1600 }
1601 if (stats.bytes_payload_tx < 190) {
1602 setverdict(fail);
1603 }
Philipp Maier36291392018-07-25 09:40:44 +02001604
1605 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001606
1607 setverdict(pass);
1608 }
1609
1610 /* Create one connection in loopback mode, test if the RTP packets are
1611 * actually reflected */
1612 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
1613 var RtpFlowData flow;
1614 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1615 var MgcpCallId call_id := '1225'H;
1616 var RtpemStats stats;
1617
1618 f_init(ep);
1619 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1620 flow.em.portnr := 10000;
1621 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
1622
1623 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1624 f_sleep(1.0);
1625 f_flow_delete(RTPEM[0], ep, call_id);
1626
1627 stats := f_rtpem_stats_get(RTPEM[0]);
1628
1629 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1630 setverdict(fail);
1631 }
1632 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1633 setverdict(fail);
1634 }
Philipp Maier36291392018-07-25 09:40:44 +02001635
1636 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001637
1638 setverdict(pass);
1639 }
1640
Philipp Maier7df85f62018-07-25 10:26:09 +02001641 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1642 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001643 var RtpFlowData flow[2];
1644 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001645 var MgcpResponse resp;
1646 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1647 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001648 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001649
1650 f_init(ep);
1651
1652 /* from us to MGW */
Philipp Maier7df85f62018-07-25 10:26:09 +02001653 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001654 /* bind local RTP emulation sockets */
1655 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001656 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001657
1658 /* from MGW back to us */
Philipp Maier7df85f62018-07-25 10:26:09 +02001659 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001660 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001661 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1662
1663 if (bidir) {
1664 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1665 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1666
1667 /* Note: When we test bidirectional we may
1668 * loose packets during switch off because
1669 * both ends are transmitting and we only
1670 * can switch them off one by one. */
1671 tolerance := 3;
1672 } else {
1673 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1674 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1675 }
1676
1677 f_sleep(1.0);
1678
1679 f_flow_delete(RTPEM[1]);
1680 f_flow_delete(RTPEM[0], ep, call_id);
1681
1682 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1683 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1684 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1685 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001686 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001687 }
1688
Philipp Maier36291392018-07-25 09:40:44 +02001689 f_rtpem_stats_err_check(stats[0]);
1690 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001691
Philipp Maier2321ef92018-06-27 17:52:04 +02001692 setverdict(pass);
1693 }
1694
1695 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1696 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001697 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001698 }
1699
1700 /* create two local RTP emulations; create two connections on MGW EP,
1701 * exchange some data in both directions */
1702 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001703 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1704 }
1705
1706 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1707 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1708 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1709 }
1710
1711 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1712 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1713 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001714 }
1715
1716 /* create two local RTP emulations and pass data in both directions */
1717 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1718 var RtpFlowData flow[2];
1719 var RtpemStats stats[2];
1720 var MgcpResponse resp;
1721 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1722 var MgcpCallId call_id := '1227'H;
1723 var integer num_pkts_tx[2];
1724 var integer temp;
1725
1726 f_init(ep);
1727
1728 /* Create the first connection in receive only mode */
1729 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1730 flow[0].em.portnr := 10000;
1731 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1732
1733 /* Create the second connection. This connection will be also
1734 * in receive only mode */
1735 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1736 flow[1].em.portnr := 20000;
1737 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], false);
1738
1739 /* The first leg starts transmitting */
1740 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1741 f_sleep(0.5);
1742 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1743 if (stats[0].num_pkts_rx_err_disabled != 0) {
1744 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001745 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001746 }
1747 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1748 if (stats[1].num_pkts_rx_err_disabled != 0) {
1749 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001750 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001751 }
1752
1753 /* The second leg starts transmitting a little later */
1754 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1755 f_sleep(1.0);
1756 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1757 if (stats[0].num_pkts_rx_err_disabled != 0) {
1758 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001759 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001760 }
1761 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1762 if (stats[1].num_pkts_rx_err_disabled != 0) {
1763 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001764 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001765 }
1766
1767 /* The first leg will now be switched into bidirectional
1768 * mode, but we do not expect any data comming back yet. */
1769 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1770 f_sleep(0.5);
1771 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1772 if (stats[1].num_pkts_rx_err_disabled != 0) {
1773 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001774 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001775 }
1776 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1777 if (stats[1].num_pkts_rx_err_disabled != 0) {
1778 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001779 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001780 }
1781
1782 /* When the second leg is switched into bidirectional mode
1783 * as well, then the MGW will connect the two together and
1784 * we should see RTP streams passing through from both ends. */
1785 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1786 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1787 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1788 num_pkts_tx[0] := stats[0].num_pkts_tx
1789 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1790 num_pkts_tx[1] := stats[1].num_pkts_tx
1791
1792 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1793 f_sleep(2.0);
1794
1795 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1796 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1797
1798 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1799 if (temp > 3 or temp < -3) {
1800 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001801 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001802 }
1803
1804 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1805 if (temp > 3 or temp < -3) {
1806 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001807 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001808 }
Philipp Maier36291392018-07-25 09:40:44 +02001809
1810 f_rtpem_stats_err_check(stats[0]);
1811 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001812
1813 /* Tear down */
1814 f_flow_delete(RTPEM[0]);
1815 f_flow_delete(RTPEM[1], ep, call_id);
1816 setverdict(pass);
1817 }
1818
1819 /* Test what happens when two RTP streams from different sources target
1820 * a single connection. Is the unsolicited stream properly ignored? */
1821 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1822 var RtpFlowData flow[2];
1823 var RtpemStats stats[2];
1824 var MgcpResponse resp;
1825 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1826 var MgcpCallId call_id := '1234321326'H;
1827 var integer unsolicited_port := 10002;
1828
1829 f_init(ep);
1830
1831 /* from us to MGW */
1832 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1833 /* bind local RTP emulation sockets */
1834 flow[0].em.portnr := 10000;
1835 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1836
1837 /* from MGW back to us */
1838 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1839 flow[1].em.portnr := 20000;
1840 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001841
1842 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1843 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1844
Philipp Maier2321ef92018-06-27 17:52:04 +02001845 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001846
Philipp Maier2321ef92018-06-27 17:52:04 +02001847 /* Start inserting unsolicited RTP packets */
Philipp Maier4de3e372018-06-29 17:15:22 +02001848 f_rtpem_bind(RTPEM[2], mp_local_ip, unsolicited_port);
1849 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001850 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1851
1852 f_sleep(0.5);
1853
Daniel Willmanna069d382018-12-13 13:53:33 +01001854 /* Stop transmitting packets and tear down the flows */
1855 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02001856 f_flow_delete(RTPEM[0]);
1857 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001858
1859 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1860 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1861 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1862 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001863 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001864 }
1865
Philipp Maier36291392018-07-25 09:40:44 +02001866 f_rtpem_stats_err_check(stats[0]);
1867 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02001868
Harald Weltebb7523b2018-03-29 08:52:01 +02001869 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001870 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001871
Philipp Maier2321ef92018-06-27 17:52:04 +02001872 /* Test a handover situation. We first create two connections transmit
1873 * some data bidirectionally. Then we will simulate a handover situation. */
1874 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1875 var RtpFlowData flow[2];
1876 var RtpemStats stats[3];
1877 var MgcpResponse resp;
1878 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1879 var MgcpCallId call_id := '76338'H;
1880 var integer port_old;
1881
1882 f_init(ep);
1883
1884 /* First connection (BTS) */
1885 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1886 /* bind local RTP emulation sockets */
1887 flow[0].em.portnr := 10000;
1888 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1889
1890 /* Second connection (PBX) */
1891 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1892 flow[1].em.portnr := 20000;
1893 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1894
1895 /* Normal rtp flow for one second */
1896 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1897 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1898 f_sleep(1.0);
1899
1900 /* Now switch the flow over to a new port (BTS) */
1901 port_old := flow[0].em.portnr;
1902 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001903 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02001904 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001905 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02001906
1907 /* When handing over a call, the old source may still keep
1908 * transmitting for a while. We simulate this by injecting
1909 * some unsolicited packets on the behalf of the old source,
1910 * (old remote port) */
Philipp Maier4de3e372018-06-29 17:15:22 +02001911 f_rtpem_bind(RTPEM[2], mp_local_ip, port_old);
1912 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001913 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1914 f_sleep(1.0);
1915 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
1916 f_sleep(1.0);
1917
1918 /* Terminate call */
1919 f_flow_delete(RTPEM[0]);
1920 f_flow_delete(RTPEM[1], ep, call_id);
1921
1922 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1923 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1924 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
1925 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001926 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001927 }
1928 stats[2] := f_rtpem_stats_get(RTPEM[2]);
1929 if (stats[2].num_pkts_rx_err_disabled != 0) {
1930 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02001931 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001932 }
1933
Philipp Maier36291392018-07-25 09:40:44 +02001934 f_rtpem_stats_err_check(stats[0]);
1935 f_rtpem_stats_err_check(stats[1]);
1936 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02001937
Philipp Maier2321ef92018-06-27 17:52:04 +02001938 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02001939 }
Harald Weltef53f1642017-11-18 19:57:11 +01001940
Philipp Maier6d4e0942019-02-21 17:35:01 +01001941
1942 /* create two local RTP emulations; create two connections on MGW EP, see if
1943 * exchanged data is converted bwtween ts101318 and rfc5993 */
1944 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
1945 var RtpFlowData flow[2];
1946 var RtpemStats stats[2];
1947 var MgcpResponse resp;
1948 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1949 var MgcpCallId call_id := '1226'H;
1950
1951 f_init(ep);
1952
1953 /* Turn on conversion mode */
1954 f_vty_enter_config(MGWVTY);
1955 f_vty_transceive(MGWVTY, "mgcp");
1956 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
1957
1958 /* from us to MGW */
1959 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1960 /* bind local RTP emulation sockets */
1961 flow[0].em.portnr := 10000;
1962 flow[0].rtp_cfg := c_RtpemDefaultCfg;
1963 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1964 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1965 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1966 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1967
1968 /* from MGW back to us */
1969 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1970 flow[1].em.portnr := 20000;
1971 flow[1].rtp_cfg := c_RtpemDefaultCfg;
1972 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
1973 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1974 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1975 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1976
1977 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1978 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1979
1980 f_sleep(1.0);
1981
1982 f_flow_delete(RTPEM[0]);
1983 f_flow_delete(RTPEM[1], ep, call_id);
1984
1985 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1986 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1987
1988 f_rtpem_stats_err_check(stats[0]);
1989 f_rtpem_stats_err_check(stats[1]);
1990
1991 /* Turn off conversion mode */
1992 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
1993
1994 setverdict(pass);
1995 }
1996
Philipp Maier4f764ce2019-03-07 10:54:10 +01001997 /* create two local RTP emulations; create two connections on MGW EP, see if
1998 * exchanged data is converted between AMR octet-aligned and bandwith
1999 * efficient-mode */
2000 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2001 var RtpFlowData flow[2];
2002 var RtpemStats stats[2];
2003 var MgcpResponse resp;
2004 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2005 var MgcpCallId call_id := '1226'H;
2006
2007 f_init(ep);
2008
2009 /* from us to MGW */
2010 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
2011 /* bind local RTP emulation sockets */
2012 flow[0].em.portnr := 10000;
2013 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2014 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2015 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2016 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2017 flow[0].fmtp := fmtp0;
2018 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2019
2020 /* from MGW back to us */
2021 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
2022 flow[1].em.portnr := 20000;
2023 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2024 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2025 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2026 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2027 flow[1].fmtp := fmtp1;
2028 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2029
2030 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2031 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2032
2033 f_sleep(1.0);
2034
2035 f_flow_delete(RTPEM[0]);
2036 f_flow_delete(RTPEM[1], ep, call_id);
2037
2038 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2039 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2040
2041 f_rtpem_stats_err_check(stats[0]);
2042 f_rtpem_stats_err_check(stats[1]);
2043
2044 setverdict(pass);
2045 }
2046
2047 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
2048 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=1", "octet-align=0");
2049 }
2050
2051 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2052 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2053 }
2054
2055 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2056 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2057 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002058
Harald Weltee636afd2017-09-17 16:24:09 +08002059 /* TODO: Double-DLCX (no retransmission) */
2060
2061
2062
2063 /* TODO: AUEP (various) */
2064 /* TODO: RSIP (various) */
2065 /* TODO: RQNT (various) */
2066 /* TODO: EPCF (various) */
2067 /* TODO: AUCX (various) */
2068 /* TODO: invalid verb (various) */
2069
Oliver Smith021141e2019-06-25 12:09:01 +02002070
2071 testcase TC_conn_timeout() runs on dummy_CT {
2072 var RtpFlowData flow;
2073 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2074 var MgcpCallId call_id := '1225'H;
2075 var MGCP_RecvFrom mrf;
2076
2077 f_init(ep);
2078 log("Setting conn-timeout to 1s");
2079 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2080
2081 log("Sending RTP data for 1.5s");
2082 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
2083 flow.em.portnr := 10000;
2084 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2085 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2086 f_sleep(1.5);
2087
2088 log("Stopping for 0.5s and resuming");
2089 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2090 f_sleep(0.5);
2091 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2092 f_sleep(0.1);
2093
2094 log("Stopping for 1.5s, expecting to run into timeout");
2095 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2096 f_sleep(1.5);
2097
2098 log("Resuming should fail now");
2099 f_rtpem_conn_refuse_expect(RTPEM[0]);
2100 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2101 f_sleep(0.2);
2102 f_rtpem_conn_refuse_verify(RTPEM[0]);
2103
2104 setverdict(pass);
2105 }
2106
Harald Welte00a067f2017-09-13 23:27:17 +02002107 control {
2108 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002109 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002110 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002111 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002112 execute(TC_crcx_unsupp_mode());
2113 execute(TC_crcx_early_bidir_mode());
2114 execute(TC_crcx_unsupp_param());
2115 execute(TC_crcx_missing_callid());
2116 execute(TC_crcx_missing_mode());
2117 execute(TC_crcx_unsupp_packet_intv());
2118 execute(TC_crcx_illegal_double_lco());
2119 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002120 execute(TC_crcx_wildcarded());
2121 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002122 execute(TC_mdcx_without_crcx());
2123 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002124 execute(TC_mdcx_wildcarded());
2125 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002126 execute(TC_crcx_and_dlcx_ep_callid_connid());
2127 execute(TC_crcx_and_dlcx_ep_callid());
2128 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002129 execute(TC_crcx_and_dlcx_ep_callid_inval());
2130 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002131 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002132
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002133 execute(TC_crcx_osmux_wildcard());
2134 execute(TC_crcx_osmux_fixed());
2135 execute(TC_crcx_osmux_fixed_twice());
2136 execute(TC_one_crcx_receive_only_osmux());
2137 execute(TC_one_crcx_loopback_osmux());
2138 execute(TC_two_crcx_and_rtp_osmux());
2139 execute(TC_two_crcx_and_rtp_osmux_bidir());
2140 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2141 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2142
Harald Welte33d82162017-12-28 03:21:57 +01002143 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002144
2145 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002146
2147 execute(TC_one_crcx_receive_only_rtp());
2148 execute(TC_one_crcx_loopback_rtp());
Harald Weltebb7523b2018-03-29 08:52:01 +02002149 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002150 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002151 execute(TC_two_crcx_diff_pt_and_rtp());
2152 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002153 execute(TC_two_crcx_mdcx_and_rtp());
2154 execute(TC_two_crcx_and_unsolicited_rtp());
2155 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002156 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002157 execute(TC_amr_oa_bwe_rtp_conversion());
2158 execute(TC_amr_oa_oa_rtp_conversion());
2159 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002160
2161 execute(TC_conn_timeout());
Harald Welte00a067f2017-09-13 23:27:17 +02002162 }
2163}