blob: deb9f3ebc1743d533a66e0ba1e4ad8cfc9fa14d0 [file] [log] [blame]
Harald Welte34b5a952019-05-27 11:54:11 +02001/* MGW (Media Gateway) test suite in TTCN-3
2 * (C) 2017-2018 Harald Welte <laforge@gnumonks.org>
3 * (C) 2018-2019 sysmocom - s.f.m.c. GmbH
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
Harald Welte00a067f2017-09-13 23:27:17 +020012module MGCP_Test {
Harald Weltef07c2862017-11-18 17:16:24 +010013 import from Osmocom_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +020014 import from MGCP_Types all;
Harald Welte4029e8c2017-11-23 22:00:42 +010015 import from MGCP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080016 import from SDP_Types all;
17 import from MGCP_CodecPort all;
18 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +010019 import from RTP_CodecPort all;
20 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020021 import from RTP_Emulation all;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020022 import from OSMUX_Types all;
23 import from OSMUX_CodecPort all;
24 import from OSMUX_CodecPort_CtrlFunct all;
25 import from OSMUX_Emulation all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080026 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010027 import from General_Types all;
28 import from Native_Functions all;
29 import from IPCP_Types all;
30 import from IP_Types all;
31 import from Osmocom_VTY_Functions all;
32 import from TELNETasp_PortType all;
Harald Welte00a067f2017-09-13 23:27:17 +020033
Philipp Maierbb7a01c2018-02-01 12:32:57 +010034 const charstring c_mgw_domain := "mgw";
35 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
36
Harald Welte21ba5572017-09-19 17:55:05 +080037 /* any variables declared in the component will be available to
38 * all functions that 'run on' the named component, similar to
39 * class members in C++ */
Harald Welte00a067f2017-09-13 23:27:17 +020040 type component dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080041 port MGCP_CODEC_PT MGCP;
42 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010043 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080044 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010045
Philipp Maier2321ef92018-06-27 17:52:04 +020046 var RTP_Emulation_CT vc_RTPEM[3];
47 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010048
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020049 var OSMUX_Emulation_CT vc_OsmuxEM;
50 port OsmuxEM_CTRL_PT OsmuxEM;
51
Philipp Maier6137c322019-02-20 16:13:41 +010052 port TELNETasp_PT MGWVTY;
Harald Welte00a067f2017-09-13 23:27:17 +020053 };
54
Harald Weltee1e18c52017-09-17 16:23:07 +080055 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
56 var MgcpTransId tid := int2str(g_trans_id);
57 g_trans_id := g_trans_id + 1;
58 return tid;
59 }
60
Harald Welte21ba5572017-09-19 17:55:05 +080061 /* all parameters declared here can be modified / overridden by
62 * the config file in the [MODULE_PARAMETERS] section. If no
63 * config file is used or the file doesn't specify them, the
64 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080065 modulepar {
66 PortNumber mp_local_udp_port := 2727;
67 charstring mp_local_ip := "127.0.0.1";
68 PortNumber mp_remote_udp_port := 2427;
69 charstring mp_remote_ip := "127.0.0.1";
Harald Weltef07c2862017-11-18 17:16:24 +010070 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020071 PortNumber mp_local_osmux_port := 1985;
Harald Welte3c6ebb92017-09-16 00:56:57 +080072 }
73
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020074 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
75 /* Turn on conversion mode */
76 f_vty_enter_config(MGWVTY);
77 f_vty_transceive(MGWVTY, "mgcp");
78 if (osmux_on) {
79 f_vty_transceive(MGWVTY, "osmux on");
80 } else {
81 f_vty_transceive(MGWVTY, "osmux off");
82 }
83 f_vty_transceive(MGWVTY, "exit");
84 f_vty_transceive(MGWVTY, "exit");
85
86 }
87
88 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +010089 map(self:MGWVTY, system:MGWVTY);
90 f_vty_set_prompts(MGWVTY);
91 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020092
93 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +010094 }
95
Harald Weltebb7523b2018-03-29 08:52:01 +020096 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
97 runs on dummy_CT {
98 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
99 map(comp_ref:RTP, system:RTP);
100 map(comp_ref:RTCP, system:RTCP);
101 comp_ref.start(RTP_Emulation.f_main());
102 }
103
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200104 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
105 runs on dummy_CT {
106 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
107 map(comp_ref:OSMUX, system:OSMUX);
108 comp_ref.start(OSMUX_Emulation.f_main());
109 }
110
Harald Welte21ba5572017-09-19 17:55:05 +0800111 /* initialization function, called by each test case at the
112 * beginning, but 'initialized' variable ensures its body is
113 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200114 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800115 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100116 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200117
Harald Welteedc45c12017-11-18 19:15:05 +0100118 if (initialized == false) {
119 initialized := true;
120
121 /* some random number for the initial transaction id */
122 g_trans_id := float2int(rnd()*65535.0);
123 map(self:MGCP, system:MGCP_CODEC_PT);
124 /* connect the MGCP test port using the given
125 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
126 * */
127 res := MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, mp_remote_ip, mp_remote_udp_port, mp_local_ip, mp_local_udp_port, 0, { udp := {} });
Harald Welte9220f632018-05-23 20:27:02 +0200128 if (not ispresent(res.connId)) {
129 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200130 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200131 }
Harald Welteedc45c12017-11-18 19:15:05 +0100132 g_mgcp_conn_id := res.connId;
133
Harald Weltebb7523b2018-03-29 08:52:01 +0200134 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
135 f_rtpem_init(vc_RTPEM[i], i);
136 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
137 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200138 if (osmux_on) {
139 f_osmuxem_init(vc_OsmuxEM);
140 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
141 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800142 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800143
Harald Welteedc45c12017-11-18 19:15:05 +0100144 if (isvalue(ep)) {
145 /* do a DLCX on all connections of the EP */
146 f_dlcx_ignore(valueof(ep));
147 }
Philipp Maier6137c322019-02-20 16:13:41 +0100148
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200149 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800150 }
151
Harald Welte00a067f2017-09-13 23:27:17 +0200152 testcase TC_selftest() runs on dummy_CT {
153 const charstring c_auep := "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100154 const charstring c_mdcx3 := "MDCX 18983215 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n";
Harald Welte00a067f2017-09-13 23:27:17 +0200155 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
156 "I: 1\n" &
157 "\n" &
158 "v=0\r\n" &
159 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
160 "s=-\r\n" &
161 "c=IN IP4 0.0.0.0\r\n" &
162 "t=0 0\r\n" &
163 "m=audio 0 RTP/AVP 126\r\n" &
164 "a=rtpmap:126 AMR/8000\r\n" &
165 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100166 const charstring c_mdcx4 := "MDCX 18983216 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200167 "M: sendrecv\r" &
168 "C: 2\r\n" &
169 "I: 1\r\n" &
170 "L: p:20, a:AMR, nt:IN\r\n" &
171 "\n" &
172 "v=0\r\n" &
173 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800174 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200175 "c=IN IP4 0.0.0.0\r\n" &
176 "t=0 0\r\n" &
177 "m=audio 4441 RTP/AVP 99\r\n" &
178 "a=rtpmap:99 AMR/8000\r\n" &
179 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800180 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200181
182 log(c_auep);
183 log(dec_MgcpCommand(c_auep));
184
185 log(c_mdcx3);
186 log(dec_MgcpCommand(c_mdcx3));
187
188 log(c_mdcx3_ret);
189 log(dec_MgcpResponse(c_mdcx3_ret));
190
191 log(c_mdcx4);
192 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800193
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100194 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
195 log(enc_MgcpCommand(valueof(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H))));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800196
197 log(c_crcx510_ret);
198 log(dec_MgcpResponse(c_crcx510_ret));
199 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100200
201 /* We didn't encounter any DTE, so pass the test */
202 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800203 }
204
Harald Weltee636afd2017-09-17 16:24:09 +0800205 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100206 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800207 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
208 * - CRCX with remote session description and without
209 *
210 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100211 * x packetization != 20ms
212 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800213 * x unsupported mode (517)
214 * x bidirectional mode before RemoteConnDesc: 527
215 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100216 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800217 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
218 */
219
Harald Welte21ba5572017-09-19 17:55:05 +0800220 /* build a receive template for receiving a MGCP message. You
221 * pass the MGCP response template in, and it will generate an
222 * MGCP_RecvFrom template that can match the primitives arriving on the
223 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800224 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
225 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100226 connId := g_mgcp_conn_id,
Harald Weltee636afd2017-09-17 16:24:09 +0800227 remName := mp_remote_ip,
228 remPort := mp_remote_udp_port,
229 locName := mp_local_ip,
230 locPort := mp_local_udp_port,
231 msg := { response := resp }
232 }
233 return mrf;
234 }
235
236 /* Send a MGCP request + receive a (matching!) response */
237 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
238 var MgcpMessage msg := { command := valueof(cmd) };
239 resp.line.trans_id := cmd.line.trans_id;
240 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800241 var MGCP_RecvFrom mrf;
242 timer T := 5.0;
243
Harald Welte55015362017-11-18 16:02:42 +0100244 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800245 T.start;
246 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800247 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200248 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
249 setverdict(fail, "Response didn't match template");
250 mtc.stop;
251 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800252 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200253 [] T.timeout {
254 setverdict(fail, "Timeout waiting for response to ", cmd);
255 mtc.stop;
256 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800257 }
258 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800259
260 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
261 return mrf.msg.response;
262 } else {
263 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
264 return r;
265 }
Harald Welte00a067f2017-09-13 23:27:17 +0200266 }
267
Harald Welteba62c8c2017-11-18 18:26:49 +0100268 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
269 var integer i;
270 for (i := 0; i < lengthof(resp.params); i := i + 1) {
271 var MgcpParameter par := resp.params[i];
272 if (par.code == "I") {
273 return str2hex(par.val);
274 }
275 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200276 setverdict(fail, "Could not find conn id for MgcpReponse");
277 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100278 return '00000000'H;
279 }
280
Harald Welte10889c12017-11-18 19:40:31 +0100281 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
282 template MgcpCallId call_id := omit,
283 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100284 var template MgcpCommand cmd;
285 var MgcpResponse resp;
286 var template MgcpResponse rtmpl := {
287 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100288 code := ret_code,
289 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100290 },
291 params := *,
292 sdp := *
293 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100294 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100295 resp := mgcp_transceive_mgw(cmd, rtmpl);
296 }
297
Harald Welte10889c12017-11-18 19:40:31 +0100298 /* Send DLCX and expect OK response */
299 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
300 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100301 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100302 }
303
Harald Welteba62c8c2017-11-18 18:26:49 +0100304 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100305 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100306 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100307 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100308 }
309
Harald Weltebb7523b2018-03-29 08:52:01 +0200310 type record HostPort {
311 charstring hostname,
312 integer portnr optional
313 }
314 type record RtpFlowData {
315 HostPort em, /* emulation side */
316 HostPort mgw, /* mgw side */
317 uint7_t pt,
318 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200319 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100320 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200321 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
322 MgcpOsmuxCID osmux_cid optional,
323 MgcpOsmuxCID osmux_cid_response optional,
324 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100325 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200326 }
327
Philipp Maier2321ef92018-06-27 17:52:04 +0200328 /* Create an RTP flow (bidirectional, or receive-only) */
329 function f_flow_create(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
Harald Weltebb7523b2018-03-29 08:52:01 +0200330 boolean one_phase := true)
331 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200332 var template MgcpCommand cmd;
333 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100334 var SDP_attribute_list attributes;
335
336 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
337 if (isvalue(flow.fmtp)) {
338 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
339 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200340
341 /* bind local RTP emulation socket */
342 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
343
Philipp Maier28bb8292018-07-20 17:09:17 +0200344 /* configure rtp-emulation */
345 if (ispresent(flow.rtp_cfg)) {
346 f_rtpem_configure(pt, flow.rtp_cfg);
347 } else {
348 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
349 rtp_cfg.tx_payload_type := flow.pt
350 f_rtpem_configure(pt, rtp_cfg);
351 }
352
Harald Weltebb7523b2018-03-29 08:52:01 +0200353 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200354 /* Connect flow to MGW using a CRCX that also contains an SDP
355 * part that tells the MGW where we are listening for RTP streams
356 * that come from the MGW. We get a fully working connection in
357 * one go. */
358
359 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200360 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100361 flow.em.portnr, { int2str(flow.pt) }, attributes);
362
Harald Weltebb7523b2018-03-29 08:52:01 +0200363 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
364 flow.mgcp_conn_id := extract_conn_id(resp);
365 /* extract port number from response */
366 flow.mgw.portnr :=
367 resp.sdp.media_list[0].media_field.ports.port_number;
368 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200369 /* Create a half-open connection only. We do not tell the MGW
370 * where it can send RTP streams to us. This means this
371 * connection will only be able to receive but can not send
372 * data back to us. In order to turn the connection in a fully
373 * bi-directional one, a separate MDCX is needed. */
374
375 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200376 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
377 flow.mgcp_conn_id := extract_conn_id(resp);
378 /* extract MGW-side port number from response */
379 flow.mgw.portnr :=
380 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200381 }
382 /* finally, connect the emulation-side RTP socket to the MGW */
383 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
384 }
385
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200386 /* Create an Osmux flow (bidirectional, or receive-only) */
387 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
388 boolean one_phase := true)
389 runs on dummy_CT {
390 var template MgcpCommand cmd;
391 var MgcpResponse resp;
392 var SDP_attribute_list attributes;
393 var OsmuxTxHandle tx_hdl;
394 var OsmuxRxHandle rx_hdl;
395 var charstring cid_response;
396 var OsmuxCID cid_resp_parsed
397
398 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
399 if (isvalue(flow.fmtp)) {
400 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
401 }
402
403 /* bind local Osmux emulation socket */
404 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
405
406 /* configure osmux-emulation */
407 if (ispresent(flow.osmux_cfg)) {
408 f_osmuxem_configure(pt, flow.osmux_cfg);
409 } else {
410 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
411 f_osmuxem_configure(pt, osmux_cfg);
412 flow.osmux_cfg := osmux_cfg
413 }
414
415 if (one_phase) {
416 /* Connect flow to MGW using a CRCX that also contains an SDP
417 * part that tells the MGW where we are listening for Osmux streams
418 * that come from the MGW. We get a fully working connection in
419 * one go. */
Pau Espin Pedrol71387aa2020-09-08 14:27:43 +0200420 if (flow.osmux_cid != -1) {
421 /* We may still want to negotiate osmux CID later at MDCX */
422 rx_hdl := c_OsmuxemDefaultRxHandle;
423 rx_hdl.cid := flow.osmux_cid;
424 f_osmuxem_register_rxhandle(pt, rx_hdl);
425 flow.osmux_cid_sent := true;
426 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200427 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
428 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
429 flow.em.portnr, { int2str(flow.pt) }, attributes);
430 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
431 flow.mgcp_conn_id := extract_conn_id(resp);
432 /* extract port number from response */
433 flow.mgw.portnr :=
434 resp.sdp.media_list[0].media_field.ports.port_number;
435 } else {
436 /* Create a half-open connection only. We do not tell the MGW
437 * where it can send Osmux streams to us. This means this
438 * connection will only be able to receive but can not send
439 * data back to us. In order to turn the connection in a fully
440 * bi-directional one, a separate MDCX is needed. */
441
442 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
443 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
444
445 flow.mgcp_conn_id := extract_conn_id(resp);
446 /* extract MGW-side port number from response */
447 flow.mgw.portnr :=
448 resp.sdp.media_list[0].media_field.ports.port_number;
449 }
450
451 /* extract Osmux CID we got assigned by the MGW */
452 var MgcpMessage resp_msg := {
453 response := resp
454 }
455
456 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
457 setverdict(fail, "No Osmux CID in MGCP response", resp);
458 mtc.stop;
459 }
460
461 /* Make sure response is no wildcard */
462 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
463 if (flow.osmux_cid_response == -1) {
464 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
465 mtc.stop;
466 }
467 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
468 f_osmuxem_register_txhandle(pt, tx_hdl);
469
470 /* finally, connect the emulation-side RTP socket to the MGW */
471 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
472 }
473
Philipp Maier2321ef92018-06-27 17:52:04 +0200474 /* Modify an existing RTP flow */
475 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
476 runs on dummy_CT {
477 var template MgcpCommand cmd;
478 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100479 var SDP_attribute_list attributes;
480
481 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
482 if (isvalue(flow.fmtp)) {
483 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
484 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200485
486 /* rebind local RTP emulation socket to the new address */
487 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
488
Philipp Maier28bb8292018-07-20 17:09:17 +0200489 /* reconfigure rtp-emulation */
490 if (ispresent(flow.rtp_cfg)) {
491 f_rtpem_configure(pt, flow.rtp_cfg);
492 } else {
493 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
494 rtp_cfg.tx_payload_type := flow.pt
495 f_rtpem_configure(pt, rtp_cfg);
496 }
497
Philipp Maier2321ef92018-06-27 17:52:04 +0200498 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
499 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
500 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100501 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200502 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
503
504 /* extract MGW-side port number from response. (usually this
505 * will not change, but thats is up to the MGW) */
506 flow.mgw.portnr :=
507 resp.sdp.media_list[0].media_field.ports.port_number;
508
509 /* reconnect the emulation-side RTP socket to the MGW */
510 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
511 }
512
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200513 /* Modify an existing Osmux flow */
514 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
515 runs on dummy_CT {
516 var template MgcpCommand cmd;
517 var MgcpResponse resp;
518 var SDP_attribute_list attributes;
519 var OsmuxRxHandle rx_hdl;
520 var charstring cid_response;
521 var OsmuxCID cid_resp_parsed
522
523 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
524 if (isvalue(flow.fmtp)) {
525 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
526 }
527
528 /* rebind local Osmux emulation socket to the new address */
529 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
530
531 /* configure osmux-emulation */
532 if (ispresent(flow.osmux_cfg)) {
533 f_osmuxem_configure(pt, flow.osmux_cfg);
534 } else {
535 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
536 f_osmuxem_configure(pt, osmux_cfg);
537 }
538
539 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
540 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
541 rx_hdl := c_OsmuxemDefaultRxHandle;
542 rx_hdl.cid := flow.osmux_cid;
543 f_osmuxem_register_rxhandle(pt, rx_hdl);
544 flow.osmux_cid_sent := true;
545 }
546
547 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
548 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
549 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
550 flow.em.portnr, { int2str(flow.pt) }, attributes);
551 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
552
553 /* extract MGW-side port number from response. (usually this
554 * will not change, but thats is up to the MGW) */
555 flow.mgw.portnr :=
556 resp.sdp.media_list[0].media_field.ports.port_number;
557
558 /* extract Osmux CID we got assigned by the MGW */
559 var MgcpMessage resp_msg := {
560 response := resp
561 }
562
563 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
564 setverdict(fail, "No Osmux CID in MGCP response", resp);
565 mtc.stop;
566 }
567
568 /* Make sure response is no wildcard */
569 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
570 if (cid_resp_parsed == -1) {
571 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
572 mtc.stop;
573 }
574 if (cid_resp_parsed != flow.osmux_cid_response) {
575 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
576 mtc.stop;
577 }
578
579 /* reconnect the emulation-side Osmux socket to the MGW */
580 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
581 }
582
Philipp Maier2321ef92018-06-27 17:52:04 +0200583 /* Delete an existing RTP flow */
584 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
585 runs on dummy_CT {
586 var template MgcpCommand cmd;
587 var MgcpResponse resp;
588
589 /* Switch off RTP flow */
590 f_rtpem_mode(pt, RTPEM_MODE_NONE);
591
592 /* Delete connection on MGW (if needed) */
593 if (isvalue(call_id) and isvalue(ep)) {
594 f_sleep(0.1);
595 f_dlcx_ok(valueof(ep), call_id);
596 }
597 }
598
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200599 /* Delete an existing Osmux flow */
600 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
601 runs on dummy_CT {
602 var template MgcpCommand cmd;
603 var MgcpResponse resp;
604
605 /* Switch off Osmux flow */
606 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
607
608 /* Delete connection on MGW (if needed) */
609 if (isvalue(call_id) and isvalue(ep)) {
610 f_sleep(0.1);
611 f_dlcx_ok(valueof(ep), call_id);
612 }
613 }
614
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100615 function f_crcx(charstring ep_prefix) runs on dummy_CT {
616 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800617 var template MgcpCommand cmd;
618 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100619 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800620
Harald Welteedc45c12017-11-18 19:15:05 +0100621 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800622
Harald Welteba62c8c2017-11-18 18:26:49 +0100623 /* create the connection on the MGW */
624 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100625 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100626 extract_conn_id(resp);
627
628 /* clean-up */
629 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100630 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100631
Philipp Maier45635f42018-06-05 17:28:02 +0200632 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
633 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
634 var template MgcpCommand cmd;
635 var MgcpResponse resp;
636 var MgcpCallId call_id := '1234'H;
637
638 f_init(ep);
639
640 /* create the connection on the MGW */
641 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
642 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
643 extract_conn_id(resp);
644
645 /* clean-up */
646 f_dlcx_ok(ep, call_id);
647
648 /* See also OS#2658: Even when we omit the LCO information, we
649 expect the MGW to pick a sane payload type for us. This
650 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200651 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200652 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200653 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200654 }
655
656 /* See also OS#2658: We also expect the MGW to assign a port
657 number to us. */
658 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
659 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200660 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200661 }
662 }
663
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200664 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
665 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
666 var template MgcpCommand cmd;
667 var MgcpResponse resp;
668 var MgcpCallId call_id := '1234'H;
669 var charstring cid_response;
670
671 if (run_init) {
672 f_init(ep, true);
673 }
674
675 /* create the connection on the MGW */
676 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
677 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
678 extract_conn_id(resp);
679
680 /* extract Osmux CID we got assigned by the MGW */
681 var MgcpMessage resp_msg := {
682 response := resp
683 }
684
685 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
686 setverdict(fail, "No Osmux CID in MGCP response", resp);
687 mtc.stop;
688 }
689
690 /* Make sure response is no wildcard */
691 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
692 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
693 mtc.stop;
694 }
695
696 /* clean-up */
697 f_dlcx_ok(ep, call_id);
698 }
699
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100700 /* test valid CRCX without SDP */
701 testcase TC_crcx() runs on dummy_CT {
702 f_crcx(c_mgw_ep_rtpbridge);
703 setverdict(pass);
704 }
705
Philipp Maier45635f42018-06-05 17:28:02 +0200706 /* test valid CRCX without SDP and LCO */
707 testcase TC_crcx_no_lco() runs on dummy_CT {
708 f_crcx_no_lco(c_mgw_ep_rtpbridge);
709 setverdict(pass);
710 }
711
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100712 /* test valid CRCX without SDP (older method without endpoint prefix) */
713 testcase TC_crcx_noprefix() runs on dummy_CT {
714 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800715 setverdict(pass);
716 }
717
718 /* test CRCX with unsupported mode, expect 517 */
719 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
720 var template MgcpCommand cmd;
721 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100722 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100723 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800724 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
725
Harald Welteedc45c12017-11-18 19:15:05 +0100726 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800727
Harald Welteba62c8c2017-11-18 18:26:49 +0100728 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800729 resp := mgcp_transceive_mgw(cmd, rtmpl);
730 setverdict(pass);
731 }
732
Harald Welte21ba5572017-09-19 17:55:05 +0800733 /* test CRCX with early bi-directional mode, expect 527 as
734 * bi-diretional media can only be established once both local and
735 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800736 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
737 var template MgcpCommand cmd;
738 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100739 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100740 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800741 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
742
Harald Welteedc45c12017-11-18 19:15:05 +0100743 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800744
Harald Welteba62c8c2017-11-18 18:26:49 +0100745 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800746 resp := mgcp_transceive_mgw(cmd, rtmpl);
747 setverdict(pass);
748 }
749
750 /* test CRCX with unsupported Parameters */
751 testcase TC_crcx_unsupp_param() runs on dummy_CT {
752 var template MgcpCommand cmd;
753 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100754 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100755 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800756 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
757
Harald Welteedc45c12017-11-18 19:15:05 +0100758 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800759
Harald Welteba62c8c2017-11-18 18:26:49 +0100760 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100761 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
762 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
763
Harald Weltee636afd2017-09-17 16:24:09 +0800764 resp := mgcp_transceive_mgw(cmd, rtmpl);
765 setverdict(pass);
766 }
767
768 /* test CRCX with missing CallId */
769 testcase TC_crcx_missing_callid() runs on dummy_CT {
770 var template MgcpCommand cmd;
771 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100772 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100773 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800774
Harald Welteedc45c12017-11-18 19:15:05 +0100775 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800776
Harald Welteba62c8c2017-11-18 18:26:49 +0100777 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800778 cmd.params := {
779 t_MgcpParConnMode("recvonly"),
780 t_MgcpParLocConnOpt("p:20")
781 }
782 resp := mgcp_transceive_mgw(cmd, rtmpl);
783 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100784
Harald Weltee636afd2017-09-17 16:24:09 +0800785 }
786
787 /* test CRCX with missing Mode */
788 testcase TC_crcx_missing_mode() runs on dummy_CT {
789 var template MgcpCommand cmd;
790 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100791 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100792 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100793 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800794
Harald Welteedc45c12017-11-18 19:15:05 +0100795 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800796
Harald Welteba62c8c2017-11-18 18:26:49 +0100797 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800798 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100799 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800800 t_MgcpParLocConnOpt("p:20")
801 }
802 resp := mgcp_transceive_mgw(cmd, rtmpl);
803 setverdict(pass);
804 }
805
806 /* test CRCX with unsupported packetization interval */
807 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
808 var template MgcpCommand cmd;
809 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100810 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100811 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100812 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800813
Harald Welteedc45c12017-11-18 19:15:05 +0100814 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800815
Harald Welteba62c8c2017-11-18 18:26:49 +0100816 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100817 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800818 resp := mgcp_transceive_mgw(cmd, rtmpl);
819 setverdict(pass);
820 }
821
822 /* test CRCX with illegal double presence of local connection option */
823 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
824 var template MgcpCommand cmd;
825 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100826 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100827 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800828 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
829
Harald Welteedc45c12017-11-18 19:15:05 +0100830 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800831
Harald Welteba62c8c2017-11-18 18:26:49 +0100832 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100833 /* p:20 is permitted only once and not twice! */
834 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800835 resp := mgcp_transceive_mgw(cmd, rtmpl);
836 setverdict(pass);
837 }
838
839 /* test valid CRCX with valid SDP */
840 testcase TC_crcx_sdp() runs on dummy_CT {
841 var template MgcpCommand cmd;
842 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100843 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100844 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800845
Harald Welteedc45c12017-11-18 19:15:05 +0100846 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800847
Harald Welteba62c8c2017-11-18 18:26:49 +0100848 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800849 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
850 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
851 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100852 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100853
854 /* clean-up */
855 f_dlcx_ok(ep, call_id);
856
Harald Weltee636afd2017-09-17 16:24:09 +0800857 setverdict(pass);
858 }
859
Philipp Maier5e06cee2018-02-01 18:28:08 +0100860 /* test valid wildcarded CRCX */
861 testcase TC_crcx_wildcarded() runs on dummy_CT {
862 var template MgcpCommand cmd;
863 var MgcpResponse resp;
864 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
865 var MgcpCallId call_id := '1234'H;
866 var MgcpEndpoint ep_assigned;
867 f_init();
868
869 /* create the connection on the MGW */
870 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
871 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
872 extract_conn_id(resp);
873
874 /* extract endpoint name we got assigned by the MGW */
875 var MgcpMessage resp_msg := {
876 response := resp
877 }
878 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
879 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200880 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100881 }
882
883 /* clean-up */
884 f_dlcx_ok(ep_assigned, call_id);
885
886 setverdict(pass);
887 }
888
889 /* test valid wildcarded CRCX */
890 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
Philipp Maierc60e8472020-07-08 12:57:13 +0200891 const integer n_endpoints := 31;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100892 var integer i;
893 var template MgcpCommand cmd;
894 var MgcpResponse resp;
895 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
896 var MgcpCallId call_id := '1234'H;
897 var MgcpEndpoint ep_assigned[n_endpoints];
898 f_init();
899
900 /* Exhaust all endpoint resources on the virtual trunk */
901 for (i := 0; i < n_endpoints; i := i+1) {
902 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
903 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
904
905 /* Make sure we got a connection id */
906 extract_conn_id(resp);
907
908 var MgcpMessage resp_msg := {
909 response := resp
910 }
911 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
912 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200913 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100914 }
915 }
916
917 /* Try to allocate one more endpoint, which should fail */
918 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
919 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
920 resp := mgcp_transceive_mgw(cmd, rtmpl);
921 setverdict(pass);
922
923 /* clean-up */
924 for (i := 0; i < n_endpoints; i := i+1) {
925 f_dlcx_ok(ep_assigned[i], call_id);
926 }
927 setverdict(pass);
928 }
929
Harald Weltee636afd2017-09-17 16:24:09 +0800930 /* TODO: various SDP related bits */
931
932
933 /* TODO: CRCX with X-Osmux */
934 /* TODO: double CRCX without force_realloc */
935
936 /* TODO: MDCX (various) */
937
938 /* TODO: MDCX without CRCX first */
939 testcase TC_mdcx_without_crcx() runs on dummy_CT {
940 var template MgcpCommand cmd;
941 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100942 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100943 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800944 var template MgcpResponse rtmpl := {
945 line := {
946 /* TODO: accept/enforce better error? */
947 code := "400",
948 string := ?
949 },
950 params:= { },
951 sdp := omit
952 };
953
Harald Welteedc45c12017-11-18 19:15:05 +0100954 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800955
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100956 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800957 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
958 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
959 valueof(ts_SDP_ptime(20)) });
960 resp := mgcp_transceive_mgw(cmd, rtmpl);
961 setverdict(pass);
962 }
963
964 /* DLCX without CRCX first */
965 testcase TC_dlcx_without_crcx() runs on dummy_CT {
966 var template MgcpCommand cmd;
967 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100968 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800969 var template MgcpResponse rtmpl := {
970 line := {
Harald Weltef91edf32017-12-28 14:16:21 +0100971 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +0800972 string := ?
973 },
974 params:= { },
975 sdp := omit
976 };
977
Harald Welteedc45c12017-11-18 19:15:05 +0100978 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800979
Harald Welteedc45c12017-11-18 19:15:05 +0100980 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800981 resp := mgcp_transceive_mgw(cmd, rtmpl);
982 setverdict(pass);
983 }
984
Philipp Maier8a3dc922018-02-02 14:55:12 +0100985 /* test valid wildcarded MDCX */
986 testcase TC_mdcx_wildcarded() runs on dummy_CT {
987 /* Note: A wildcarded MDCX is not allowed, so we expect the
988 * MGW to reject this request */
989 var template MgcpCommand cmd;
990 var MgcpResponse resp;
991 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
992 var MgcpCallId call_id := '1225'H;
993 var template MgcpResponse rtmpl := {
994 line := {
995 /* TODO: accept/enforce better error? */
996 code := "507",
997 string := ?
998 },
999 params:= { },
1000 sdp := omit
1001 };
1002
1003 f_init(ep);
1004
1005 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
1006 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
1007 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
1008 valueof(ts_SDP_ptime(20)) });
1009 resp := mgcp_transceive_mgw(cmd, rtmpl);
1010 setverdict(pass);
1011 }
1012
1013 /* test valid wildcarded DLCX */
1014 testcase TC_dlcx_wildcarded() runs on dummy_CT {
1015 /* Note: A wildcarded DLCX is specified, but our MGW does not
1016 * support this feature so we expect the MGW to reject the
1017 * request */
1018 var template MgcpCommand cmd;
1019 var MgcpResponse resp;
1020 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1021 var template MgcpResponse rtmpl := {
1022 line := {
1023 code := "507",
1024 string := ?
1025 },
1026 params:= { },
1027 sdp := omit
1028 };
1029
1030 f_init(ep);
1031
1032 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1033 resp := mgcp_transceive_mgw(cmd, rtmpl);
1034 setverdict(pass);
1035 }
1036
Harald Welte79181ff2017-11-18 19:26:11 +01001037 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1038 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1039 var template MgcpCommand cmd;
1040 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001041 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001042 var MgcpCallId call_id := '51234'H;
1043
1044 f_init(ep);
1045
1046 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1047 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1048
1049 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1050
1051 setverdict(pass);
1052 }
1053
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001054 /* test valid CRCX without SDP */
1055 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1056 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1057 setverdict(pass);
1058 }
1059
1060 /* test valid CRCX without SDP */
1061 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1062 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1063 setverdict(pass);
1064 }
1065
1066 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1067 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1068 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1069 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1070 setverdict(pass);
1071 }
1072
1073 /* Create one half open connection in receive-only mode. The MGW must accept
1074 * the packets but must not send any. */
1075 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1076 var RtpFlowData flow;
1077 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1078 var MgcpCallId call_id := '1225'H;
1079 var OsmuxemStats stats;
1080 var OsmuxTxHandle tx_hdl;
1081
1082 f_init(ep, true);
1083 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
1084 flow.em.portnr := mp_local_osmux_port;
1085 flow.osmux_cid := -1;
1086 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1087
1088 /* create a transmitter not yet known by MGW */
1089 tx_hdl := valueof(t_TxHandleAMR590(2));
1090 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1091
1092 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1093 f_sleep(1.0);
1094 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1095
1096 stats := f_osmuxem_stats_get(OsmuxEM);
1097
1098 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1099 setverdict(fail);
1100 }
1101 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1102 setverdict(fail);
1103 }
1104
1105 f_osmuxem_stats_err_check(stats);
1106
1107 setverdict(pass);
1108 }
1109
1110 /* Create one connection in loopback mode, test if the Osmux packets are
1111 * actually reflected */
1112 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1113 var RtpFlowData flow;
1114 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1115 var MgcpCallId call_id := '1225'H;
1116 var OsmuxemStats stats;
1117 var OsmuxTxHandle tx_hdl;
1118
1119 f_init(ep, true);
1120 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1121 flow.em.portnr := mp_local_osmux_port;
1122 flow.osmux_cid := 2;
1123 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1124
1125 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1126 f_sleep(1.0);
1127
1128 /* Switch off both Tx, wait to receive delayed frames from MGW */
1129 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1130 f_sleep(0.1);
1131 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1132
1133 stats := f_osmuxem_stats_get(OsmuxEM);
1134
1135 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1136 setverdict(fail);
1137 }
1138 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1139 setverdict(fail);
1140 }
1141
1142 f_osmuxem_stats_err_check(stats);
1143
1144 setverdict(pass);
1145 }
1146
1147 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1148 * must match the reception statistics on the other side and vice versa. The
1149 * user may also supply a tolerance value (number of packets) when deviations
1150 * are acceptable */
1151 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1152 var integer plen;
1153
1154 log("stats A: ", a);
1155 log("stats B: ", b);
1156 log("tolerance: ", tolerance, " packets");
1157 log("batch_size: ", batch_size, " packets");
1158
1159 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1160
1161 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1162 return false;
1163 }
1164
1165 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1166 return false;
1167 }
1168
1169 if(a.num_pkts_tx > 0) {
1170 plen := a.bytes_payload_tx / a.num_pkts_tx;
1171 } else {
1172 plen := 0;
1173 }
1174
1175 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1176 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1177 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1178 return false;
1179 }
1180
1181 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) {
1182 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1183 return false;
1184 }
1185
1186 return true;
1187 }
1188
1189 function f_TC_two_crcx_and_rtp_osmux(boolean bidir) runs on dummy_CT {
1190 var RtpFlowData flow[2];
1191 var RtpemStats stats_rtp;
1192 var OsmuxemStats stats_osmux;
1193 var MgcpResponse resp;
1194 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1195 var MgcpCallId call_id := '1226'H;
1196 var integer tolerance := 0;
1197
1198 f_init(ep, true);
1199
1200 /* from us to MGW */
1201 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1202 flow[0].rtp_cfg := c_RtpemDefaultCfg
1203 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1204 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1205 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);
1206 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1207 /* bind local RTP emulation sockets */
1208 flow[0].em.portnr := 10000;
1209 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1210
1211 /* from MGW back to us */
1212 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "AMR/8000"));
1213 flow[1].em.portnr := mp_local_osmux_port;
1214 flow[1].osmux_cid := 2;
1215 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1216 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1217
1218 if (bidir) {
1219 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1220 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1221
1222 /* Note: When we test bidirectional we may
1223 * loose packets during switch off because
1224 * both ends are transmitting and we only
1225 * can switch them off one by one. */
1226 tolerance := 3;
1227 } else {
1228 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1229 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1230 }
1231
1232 f_sleep(1.0);
1233
1234 /* Switch off both Tx, wait to receive delayed frames from MGW */
1235 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1236 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1237 f_sleep(0.1);
1238
1239 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1240 f_flow_delete(RTPEM[1]);
1241
1242 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1243 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1244 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1245 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1246 mtc.stop;
1247 }
1248
1249 f_rtpem_stats_err_check(stats_rtp);
1250 f_osmuxem_stats_err_check(stats_osmux);
1251
1252 setverdict(pass);
1253 }
1254
1255 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1256 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
1257 f_TC_two_crcx_and_rtp_osmux(false);
1258 }
1259
1260 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1261 * exchange some data in both directions */
1262 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
1263 f_TC_two_crcx_and_rtp_osmux(true);
1264 }
1265
1266
1267 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard) runs on dummy_CT {
1268 var RtpFlowData flow[2];
1269 var RtpemStats stats_rtp;
1270 var OsmuxemStats stats_osmux;
1271 var MgcpResponse resp;
1272 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1273 var MgcpCallId call_id := '1227'H;
1274 var integer num_pkts_tx[2];
1275 var integer temp;
1276
1277 f_init(ep, true);
1278
1279 /* Create the first connection in receive only mode */
1280 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1281 flow[0].rtp_cfg := c_RtpemDefaultCfg
1282 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1283 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1284 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);
1285 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1286 /* bind local RTP emulation sockets */
1287 flow[0].em.portnr := 10000;
1288 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1289
1290
1291 /* Create the second connection. This connection will be also
1292 * in receive only mode */
1293 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "AMR/8000"));
1294 flow[1].em.portnr := mp_local_osmux_port;
1295 if (crcx_osmux_wildcard) {
1296 flow[1].osmux_cid := -1;
1297 } else {
1298 flow[1].osmux_cid := 2;
1299 }
1300 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1301 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], false);
1302
1303
1304 /* The first leg starts transmitting */
1305 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1306 f_sleep(0.5);
1307 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1308 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1309 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1310 mtc.stop;
1311 }
1312 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1313 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1314 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1315 mtc.stop;
1316 }
1317
1318 /* The second leg starts transmitting a little later */
1319 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1320 f_sleep(1.0);
1321 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1322 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1323 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1324 mtc.stop;
1325 }
1326 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1327 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1328 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1329 mtc.stop;
1330 }
1331
1332 /* The first leg will now be switched into bidirectional
1333 * mode, but we do not expect any data comming back yet. */
1334 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1335 f_sleep(0.5);
1336 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1337 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1338 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1339 mtc.stop;
1340 }
1341 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1342 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1343 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1344 mtc.stop;
1345 }
1346
1347 /* When the second leg is switched into bidirectional mode
1348 * as well, then the MGW will connect the two together and
1349 * we should see RTP streams passing through from both ends. */
1350 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1351 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1352 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1353 num_pkts_tx[0] := stats_rtp.num_pkts_tx
1354 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1355 num_pkts_tx[1] := stats_osmux.num_pkts_tx
1356
1357 if (crcx_osmux_wildcard) {
1358 /* For now we must set same CID as the MGW recvCID,
1359 * having sendCID!=recvCID is not yet supported. */
1360 flow[1].osmux_cid := flow[1].osmux_cid_response;
1361 }
1362 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1363 f_sleep(2.0);
1364
1365 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1366 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1367
1368 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1369 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1370 log("stats_rtp: ", stats_rtp);
1371 log("stats_osmux: ", stats_osmux);
1372 log("old_rtp_tx: ", num_pkts_tx[0]);
1373 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1374 mtc.stop;
1375 }
1376
1377 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1378 if (temp > 3 or temp < -3) {
1379 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1380 mtc.stop;
1381 }
1382
1383 f_rtpem_stats_err_check(stats_rtp);
1384 f_osmuxem_stats_err_check(stats_osmux);
1385
1386 /* Tear down */
1387 f_flow_delete(RTPEM[0]);
1388 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1389 setverdict(pass);
1390 }
1391
1392 /* create one RTP and one OSmux emulations and pass data in both
1393 directions. Create CRCX with wildcard Osmux CID and set it later
1394 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1395 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
1396 f_two_crcx_mdcx_and_rtp_osmux(true);
1397 }
1398
1399 /* create one RTP and one OSmux emulations and pass data in both
1400 directions. Create CRCX with fixed Osmux CID and keep it during
1401 MDCX. This is similar to how BSC sets up the call in AoIP. */
1402 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
1403 f_two_crcx_mdcx_and_rtp_osmux(false);
1404 }
1405
Harald Welte646ecdb2017-12-28 03:21:57 +01001406 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1407 var template MgcpCommand cmd;
1408 var MgcpResponse resp;
1409
1410 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1411 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1412
1413 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1414
1415 setverdict(pass);
1416 }
1417
1418 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1419 var MgcpEndpoint ep;
1420 var MgcpCallId call_id;
1421 var integer ep_nr;
1422
1423 f_init();
1424
1425 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maier4c2a1ea2020-05-15 18:37:05 +02001426 if(ep_nr > 15) {
1427 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
1428 } else {
1429 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 1)) & "@" & c_mgw_domain;
1430 }
Harald Welte646ecdb2017-12-28 03:21:57 +01001431 call_id := int2hex(ep_nr, 2) & '1234'H;
1432 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1433 }
1434 }
1435
Harald Welte79181ff2017-11-18 19:26:11 +01001436 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1437 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001438 var template MgcpCommand cmd;
1439 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001440 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001441 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001442
Harald Welteedc45c12017-11-18 19:15:05 +01001443 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001444
Harald Welteba62c8c2017-11-18 18:26:49 +01001445 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001446 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001447
Harald Welteba62c8c2017-11-18 18:26:49 +01001448 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001449
1450 setverdict(pass);
1451 }
1452
Harald Welte79181ff2017-11-18 19:26:11 +01001453 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1454 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1455 var template MgcpCommand cmd;
1456 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001457 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001458 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001459
1460 f_init(ep);
1461
1462 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1463 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1464
1465 f_dlcx_ok(ep);
1466
1467 setverdict(pass);
1468 }
1469
1470
Harald Welte6d167f82017-11-18 19:41:35 +01001471 /* CRCX + DLCX of valid endpoint but invalid call-id */
1472 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1473 var template MgcpCommand cmd;
1474 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001475 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001476 var MgcpCallId call_id := '51231'H;
1477
1478 f_init(ep);
1479
1480 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1481 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1482
1483 f_dlcx(ep, "516", *, 'ffff'H);
1484
1485 setverdict(pass);
1486 }
1487
1488
1489 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1490 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1491 var template MgcpCommand cmd;
1492 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001493 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001494 var MgcpCallId call_id := '51230'H;
1495
1496 f_init(ep);
1497
1498 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1499 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1500
1501 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1502
1503 setverdict(pass);
1504 }
1505
1506
Harald Weltee636afd2017-09-17 16:24:09 +08001507 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001508 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1509 var template MgcpCommand cmd;
1510 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001511 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001512 var MgcpCallId call_id := '51229'H;
1513 var template MgcpResponse rtmpl := {
1514 line := {
1515 code := "200",
1516 string := "OK"
1517 },
1518 params:= { },
1519 sdp := omit
1520 };
1521
1522 f_init(ep);
1523
1524 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1525 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1526
1527 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1528 resp := mgcp_transceive_mgw(cmd, rtmpl);
1529 resp := mgcp_transceive_mgw(cmd, rtmpl);
1530
1531 setverdict(pass);
1532 }
1533
Harald Weltebb7523b2018-03-29 08:52:01 +02001534 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1535 charstring codec) := {
1536 em := {
1537 hostname := host_a,
1538 portnr := omit
1539 },
1540 mgw := {
1541 hostname := host_b,
1542 portnr := omit
1543 },
1544 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001545 codec := codec,
1546 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001547 }
Harald Weltef53f1642017-11-18 19:57:11 +01001548
Harald Weltebb7523b2018-03-29 08:52:01 +02001549 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1550 testcase TC_rtpem_selftest() runs on dummy_CT {
1551 var RtpemStats stats[2];
1552 var integer local_port := 10000;
1553 var integer local_port2 := 20000;
1554
1555 f_init();
1556
1557 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1558 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1559
1560 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1561 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1562
1563 log("=== starting");
1564 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1565 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1566
1567 f_sleep(5.0);
1568
1569 log("=== stopping");
1570 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1571 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1572 f_sleep(0.5);
1573 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1574 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1575
1576 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1577 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1578 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1579 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001580 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001581 }
1582 setverdict(pass);
1583 }
1584
Philipp Maier2321ef92018-06-27 17:52:04 +02001585 /* Create one half open connection in receive-only mode. The MGW must accept
1586 * the packets but must not send any. */
1587 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1588 var RtpFlowData flow;
1589 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1590 var MgcpCallId call_id := '1225'H;
1591 var RtpemStats stats;
1592
1593 f_init(ep);
1594 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
1595 flow.em.portnr := 10000;
1596 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, false);
1597
1598 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1599 f_sleep(1.0);
1600 f_flow_delete(RTPEM[0], ep, call_id);
1601
1602 stats := f_rtpem_stats_get(RTPEM[0]);
1603
Philipp Maier42b17cc2019-10-01 13:53:17 +02001604 /* Make sure that at least some amount of RTP packets/bytes
1605 * have has been transmitted. The compare values for
1606 * stats.num_pkts_tx and stats.bytes_payload_tx are determined
1607 * using a testrun and the results were devided by 2, so even
1608 * in load situations we should reach the minimum amount of
1609 * required packets/bytes */
1610
1611 if (stats.num_pkts_tx < 24) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001612 setverdict(fail);
1613 }
Philipp Maier42b17cc2019-10-01 13:53:17 +02001614 if (stats.bytes_payload_tx < 96) {
Philipp Maier2321ef92018-06-27 17:52:04 +02001615 setverdict(fail);
1616 }
Philipp Maier36291392018-07-25 09:40:44 +02001617
1618 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001619
1620 setverdict(pass);
1621 }
1622
1623 /* Create one connection in loopback mode, test if the RTP packets are
1624 * actually reflected */
1625 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
1626 var RtpFlowData flow;
1627 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1628 var MgcpCallId call_id := '1225'H;
1629 var RtpemStats stats;
1630
1631 f_init(ep);
1632 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1633 flow.em.portnr := 10000;
1634 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
1635
1636 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1637 f_sleep(1.0);
1638 f_flow_delete(RTPEM[0], ep, call_id);
1639
1640 stats := f_rtpem_stats_get(RTPEM[0]);
1641
1642 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1643 setverdict(fail);
1644 }
1645 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1646 setverdict(fail);
1647 }
Philipp Maier36291392018-07-25 09:40:44 +02001648
1649 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001650
1651 setverdict(pass);
1652 }
1653
Philipp Maier7df85f62018-07-25 10:26:09 +02001654 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1655 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001656 var RtpFlowData flow[2];
1657 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001658 var MgcpResponse resp;
1659 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1660 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001661 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001662
1663 f_init(ep);
1664
1665 /* from us to MGW */
Philipp Maier7df85f62018-07-25 10:26:09 +02001666 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001667 /* bind local RTP emulation sockets */
1668 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001669 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001670
1671 /* from MGW back to us */
Philipp Maier7df85f62018-07-25 10:26:09 +02001672 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001673 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001674 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1675
1676 if (bidir) {
1677 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1678 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1679
1680 /* Note: When we test bidirectional we may
1681 * loose packets during switch off because
1682 * both ends are transmitting and we only
1683 * can switch them off one by one. */
1684 tolerance := 3;
1685 } else {
1686 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1687 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1688 }
1689
1690 f_sleep(1.0);
1691
1692 f_flow_delete(RTPEM[1]);
1693 f_flow_delete(RTPEM[0], ep, call_id);
1694
1695 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1696 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1697 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1698 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001699 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001700 }
1701
Philipp Maier36291392018-07-25 09:40:44 +02001702 f_rtpem_stats_err_check(stats[0]);
1703 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001704
Philipp Maier2321ef92018-06-27 17:52:04 +02001705 setverdict(pass);
1706 }
1707
1708 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1709 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001710 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001711 }
1712
1713 /* create two local RTP emulations; create two connections on MGW EP,
1714 * exchange some data in both directions */
1715 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001716 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1717 }
1718
1719 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1720 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1721 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1722 }
1723
1724 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1725 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1726 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001727 }
1728
1729 /* create two local RTP emulations and pass data in both directions */
1730 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1731 var RtpFlowData flow[2];
1732 var RtpemStats stats[2];
1733 var MgcpResponse resp;
1734 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1735 var MgcpCallId call_id := '1227'H;
1736 var integer num_pkts_tx[2];
1737 var integer temp;
1738
1739 f_init(ep);
1740
1741 /* Create the first connection in receive only mode */
1742 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1743 flow[0].em.portnr := 10000;
1744 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1745
1746 /* Create the second connection. This connection will be also
1747 * in receive only mode */
1748 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1749 flow[1].em.portnr := 20000;
1750 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], false);
1751
1752 /* The first leg starts transmitting */
1753 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1754 f_sleep(0.5);
1755 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1756 if (stats[0].num_pkts_rx_err_disabled != 0) {
1757 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001758 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001759 }
1760 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1761 if (stats[1].num_pkts_rx_err_disabled != 0) {
1762 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001763 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001764 }
1765
1766 /* The second leg starts transmitting a little later */
1767 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1768 f_sleep(1.0);
1769 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1770 if (stats[0].num_pkts_rx_err_disabled != 0) {
1771 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001772 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001773 }
1774 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1775 if (stats[1].num_pkts_rx_err_disabled != 0) {
1776 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001777 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001778 }
1779
1780 /* The first leg will now be switched into bidirectional
1781 * mode, but we do not expect any data comming back yet. */
1782 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1783 f_sleep(0.5);
1784 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1785 if (stats[1].num_pkts_rx_err_disabled != 0) {
1786 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001787 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001788 }
1789 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1790 if (stats[1].num_pkts_rx_err_disabled != 0) {
1791 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001792 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001793 }
1794
1795 /* When the second leg is switched into bidirectional mode
1796 * as well, then the MGW will connect the two together and
1797 * we should see RTP streams passing through from both ends. */
1798 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1799 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1800 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1801 num_pkts_tx[0] := stats[0].num_pkts_tx
1802 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1803 num_pkts_tx[1] := stats[1].num_pkts_tx
1804
1805 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1806 f_sleep(2.0);
1807
1808 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1809 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1810
1811 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1812 if (temp > 3 or temp < -3) {
1813 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001814 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001815 }
1816
1817 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1818 if (temp > 3 or temp < -3) {
1819 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001820 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001821 }
Philipp Maier36291392018-07-25 09:40:44 +02001822
1823 f_rtpem_stats_err_check(stats[0]);
1824 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001825
1826 /* Tear down */
1827 f_flow_delete(RTPEM[0]);
1828 f_flow_delete(RTPEM[1], ep, call_id);
1829 setverdict(pass);
1830 }
1831
1832 /* Test what happens when two RTP streams from different sources target
1833 * a single connection. Is the unsolicited stream properly ignored? */
1834 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1835 var RtpFlowData flow[2];
1836 var RtpemStats stats[2];
1837 var MgcpResponse resp;
1838 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1839 var MgcpCallId call_id := '1234321326'H;
1840 var integer unsolicited_port := 10002;
1841
1842 f_init(ep);
1843
1844 /* from us to MGW */
1845 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1846 /* bind local RTP emulation sockets */
1847 flow[0].em.portnr := 10000;
1848 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1849
1850 /* from MGW back to us */
1851 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1852 flow[1].em.portnr := 20000;
1853 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001854
1855 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1856 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1857
Philipp Maier2321ef92018-06-27 17:52:04 +02001858 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001859
Philipp Maier2321ef92018-06-27 17:52:04 +02001860 /* Start inserting unsolicited RTP packets */
Philipp Maier4de3e372018-06-29 17:15:22 +02001861 f_rtpem_bind(RTPEM[2], mp_local_ip, unsolicited_port);
1862 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001863 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1864
1865 f_sleep(0.5);
1866
Daniel Willmanna069d382018-12-13 13:53:33 +01001867 /* Stop transmitting packets and tear down the flows */
1868 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02001869 f_flow_delete(RTPEM[0]);
1870 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001871
1872 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1873 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1874 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1875 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001876 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001877 }
1878
Philipp Maier36291392018-07-25 09:40:44 +02001879 f_rtpem_stats_err_check(stats[0]);
1880 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02001881
Harald Weltebb7523b2018-03-29 08:52:01 +02001882 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001883 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001884
Philipp Maier2321ef92018-06-27 17:52:04 +02001885 /* Test a handover situation. We first create two connections transmit
1886 * some data bidirectionally. Then we will simulate a handover situation. */
1887 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1888 var RtpFlowData flow[2];
1889 var RtpemStats stats[3];
1890 var MgcpResponse resp;
1891 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1892 var MgcpCallId call_id := '76338'H;
1893 var integer port_old;
1894
1895 f_init(ep);
1896
1897 /* First connection (BTS) */
1898 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1899 /* bind local RTP emulation sockets */
1900 flow[0].em.portnr := 10000;
1901 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1902
1903 /* Second connection (PBX) */
1904 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1905 flow[1].em.portnr := 20000;
1906 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1907
1908 /* Normal rtp flow for one second */
1909 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1910 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1911 f_sleep(1.0);
1912
1913 /* Now switch the flow over to a new port (BTS) */
1914 port_old := flow[0].em.portnr;
1915 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001916 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02001917 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001918 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02001919
1920 /* When handing over a call, the old source may still keep
1921 * transmitting for a while. We simulate this by injecting
1922 * some unsolicited packets on the behalf of the old source,
1923 * (old remote port) */
Philipp Maier4de3e372018-06-29 17:15:22 +02001924 f_rtpem_bind(RTPEM[2], mp_local_ip, port_old);
1925 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001926 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1927 f_sleep(1.0);
1928 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
1929 f_sleep(1.0);
1930
1931 /* Terminate call */
1932 f_flow_delete(RTPEM[0]);
1933 f_flow_delete(RTPEM[1], ep, call_id);
1934
1935 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1936 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1937 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
1938 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001939 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001940 }
1941 stats[2] := f_rtpem_stats_get(RTPEM[2]);
1942 if (stats[2].num_pkts_rx_err_disabled != 0) {
1943 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02001944 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001945 }
1946
Philipp Maier36291392018-07-25 09:40:44 +02001947 f_rtpem_stats_err_check(stats[0]);
1948 f_rtpem_stats_err_check(stats[1]);
1949 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02001950
Philipp Maier2321ef92018-06-27 17:52:04 +02001951 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02001952 }
Harald Weltef53f1642017-11-18 19:57:11 +01001953
Philipp Maier6d4e0942019-02-21 17:35:01 +01001954
1955 /* create two local RTP emulations; create two connections on MGW EP, see if
1956 * exchanged data is converted bwtween ts101318 and rfc5993 */
1957 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
1958 var RtpFlowData flow[2];
1959 var RtpemStats stats[2];
1960 var MgcpResponse resp;
1961 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1962 var MgcpCallId call_id := '1226'H;
1963
1964 f_init(ep);
1965
1966 /* Turn on conversion mode */
1967 f_vty_enter_config(MGWVTY);
1968 f_vty_transceive(MGWVTY, "mgcp");
1969 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
1970
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02001971 /* Connection #0 (Bidirectional) */
Philipp Maier6d4e0942019-02-21 17:35:01 +01001972 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1973 /* bind local RTP emulation sockets */
1974 flow[0].em.portnr := 10000;
1975 flow[0].rtp_cfg := c_RtpemDefaultCfg;
1976 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1977 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1978 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1979 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1980
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02001981 /* Connection #1 (Bidirectional) */
Philipp Maier6d4e0942019-02-21 17:35:01 +01001982 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1983 flow[1].em.portnr := 20000;
1984 flow[1].rtp_cfg := c_RtpemDefaultCfg;
1985 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
1986 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1987 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1988 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1989
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02001990 /* Send RTP packets to connection #0, receive on connection #1 */
1991 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1992 f_sleep(0.5);
1993 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier6d4e0942019-02-21 17:35:01 +01001994 f_sleep(1.0);
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02001995 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1996 f_sleep(0.5);
1997 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier6d4e0942019-02-21 17:35:01 +01001998
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02001999 /* Send RTP packets to connection #1, receive on connection #0 */
2000 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2001 f_sleep(0.5);
2002 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2003 f_sleep(1.0);
2004 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2005 f_sleep(0.5);
2006 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2007
2008 /* Remove RTP flows and check statistics */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002009 f_flow_delete(RTPEM[0]);
2010 f_flow_delete(RTPEM[1], ep, call_id);
2011
Philipp Maierfaa1d2f2019-10-01 14:07:35 +02002012 /* Check for errors */
Philipp Maier6d4e0942019-02-21 17:35:01 +01002013 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2014 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier6d4e0942019-02-21 17:35:01 +01002015 f_rtpem_stats_err_check(stats[0]);
2016 f_rtpem_stats_err_check(stats[1]);
2017
2018 /* Turn off conversion mode */
2019 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
2020
2021 setverdict(pass);
2022 }
2023
Philipp Maier4f764ce2019-03-07 10:54:10 +01002024 /* create two local RTP emulations; create two connections on MGW EP, see if
2025 * exchanged data is converted between AMR octet-aligned and bandwith
2026 * efficient-mode */
2027 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
2028 var RtpFlowData flow[2];
2029 var RtpemStats stats[2];
2030 var MgcpResponse resp;
2031 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
2032 var MgcpCallId call_id := '1226'H;
2033
2034 f_init(ep);
2035
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002036 /* Connection #0 (Bidirectional) */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002037 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
2038 /* bind local RTP emulation sockets */
2039 flow[0].em.portnr := 10000;
2040 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2041 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2042 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2043 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2044 flow[0].fmtp := fmtp0;
2045 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2046
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002047 /* Connection #1 (Bidirectional) */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002048 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
2049 flow[1].em.portnr := 20000;
2050 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2051 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2052 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2053 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2054 flow[1].fmtp := fmtp1;
2055 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2056
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002057 /* Send RTP packets to connection #0, receive on connection #1 */
2058 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
2059 f_sleep(0.5);
2060 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002061 f_sleep(1.0);
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002062 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2063 f_sleep(0.5);
2064 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002065
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002066 /* Send RTP packets to connection #1, receive on connection #0 */
2067 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
2068 f_sleep(0.5);
2069 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
2070 f_sleep(1.0);
2071 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
2072 f_sleep(0.5);
2073 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2074
2075 /* Remove RTP flows and check statistics */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002076 f_flow_delete(RTPEM[0]);
2077 f_flow_delete(RTPEM[1], ep, call_id);
2078
Philipp Maier9ff0f8a2019-10-01 13:21:28 +02002079 /* Check for errors */
Philipp Maier4f764ce2019-03-07 10:54:10 +01002080 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2081 stats[1] := f_rtpem_stats_get(RTPEM[1]);
Philipp Maier4f764ce2019-03-07 10:54:10 +01002082 f_rtpem_stats_err_check(stats[0]);
2083 f_rtpem_stats_err_check(stats[1]);
2084
2085 setverdict(pass);
2086 }
2087
Philipp Maier882843d2020-05-25 15:33:13 +02002088 /* Note: The hexstrings used with the f_TC_amr_x_x_rtp_conversion test
2089 * functions are real world AMR RTP payloads including AMR header. The
2090 * payloads were extracted from a trace with known good payloads. */
2091
Philipp Maier4f764ce2019-03-07 10:54:10 +01002092 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
Philipp Maier882843d2020-05-25 15:33:13 +02002093 f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0");
Philipp Maier4f764ce2019-03-07 10:54:10 +01002094 }
2095
2096 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2097 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2098 }
2099
2100 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2101 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2102 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002103
Harald Weltee636afd2017-09-17 16:24:09 +08002104 /* TODO: Double-DLCX (no retransmission) */
2105
2106
2107
2108 /* TODO: AUEP (various) */
2109 /* TODO: RSIP (various) */
2110 /* TODO: RQNT (various) */
2111 /* TODO: EPCF (various) */
2112 /* TODO: AUCX (various) */
2113 /* TODO: invalid verb (various) */
2114
Oliver Smith021141e2019-06-25 12:09:01 +02002115
2116 testcase TC_conn_timeout() runs on dummy_CT {
2117 var RtpFlowData flow;
2118 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
2119 var MgcpCallId call_id := '1225'H;
2120 var MGCP_RecvFrom mrf;
2121
2122 f_init(ep);
2123 log("Setting conn-timeout to 1s");
2124 f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */
2125
2126 log("Sending RTP data for 1.5s");
2127 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
2128 flow.em.portnr := 10000;
2129 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2130 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2131 f_sleep(1.5);
2132
2133 log("Stopping for 0.5s and resuming");
2134 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2135 f_sleep(0.5);
2136 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2137 f_sleep(0.1);
2138
2139 log("Stopping for 1.5s, expecting to run into timeout");
2140 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
2141 f_sleep(1.5);
2142
2143 log("Resuming should fail now");
2144 f_rtpem_conn_refuse_expect(RTPEM[0]);
2145 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2146 f_sleep(0.2);
2147 f_rtpem_conn_refuse_verify(RTPEM[0]);
2148
2149 setverdict(pass);
2150 }
2151
Philipp Maier2609c752020-07-08 12:38:09 +02002152 /* Test (valid) CRCX followed by (valid) DLCX containing EP (E1) */
2153 testcase TC_e1_crcx_and_dlcx_ep() runs on dummy_CT {
2154 var template MgcpCommand cmd;
2155 var MgcpResponse resp;
2156 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2157 var MgcpCallId call_id := '8376F297'H;
2158
2159 f_init(ep);
2160
2161 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
2162 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2163
2164 f_dlcx_ok(ep);
2165
2166 setverdict(pass);
2167 }
2168
2169 /* Test what happens when overlapping endpoints are selected (E1) */
2170 testcase TC_e1_crcx_with_overlap() runs on dummy_CT {
2171 var template MgcpCommand cmd;
2172 var MgcpResponse resp;
2173 var MgcpEndpoint ep_1 := "ds/e1-1/s-1/su8-0@" & c_mgw_domain;
2174 var MgcpEndpoint ep_2 := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2175 var MgcpCallId call_id_1 := '8376F297'H;
2176 var MgcpCallId call_id_2 := '837AF2A7'H;
2177
2178 f_init();
2179
2180 /* ep_1 and ep_2 are overlapping, selecting both one after
2181 * another should work fine: */
2182 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2183 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2184 f_dlcx_ok(ep_1);
2185 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2186 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2187 f_dlcx_ok(ep_2);
2188
2189 /* When ep_1 is serving a call we can not select ep_2 becaus
2190 * it is overlapping with ep_1 */
2191 cmd := ts_CRCX(get_next_trans_id(), ep_1, "recvonly", call_id_1);
2192 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
2193 cmd := ts_CRCX(get_next_trans_id(), ep_2, "recvonly", call_id_2);
2194 resp := mgcp_transceive_mgw(cmd, ?);
2195 if (resp.line.code != "501") {
2196 setverdict(fail, "unexpected CRCX returncode, CRCX should fail!");
2197 }
2198 f_dlcx_ok(ep_1);
2199
2200 setverdict(pass);
2201 }
2202
2203 /* Create one connection in loopback mode, test if the RTP packets are
2204 * actually reflected */
2205 testcase TC_e1_crcx_loopback() runs on dummy_CT {
2206 var RtpFlowData flow;
2207 var MgcpEndpoint ep := "ds/e1-1/s-1/su16-0@" & c_mgw_domain;
2208 var MgcpCallId call_id := '12250989'H;
2209 var RtpemStats stats;
2210
2211 f_init(ep);
2212 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
2213 flow.em.portnr := 10000;
2214 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
2215
2216 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2217 f_sleep(1.0);
2218 f_flow_delete(RTPEM[0], ep, call_id);
2219
2220 stats := f_rtpem_stats_get(RTPEM[0]);
2221
2222 if (stats.num_pkts_tx != stats.num_pkts_rx) {
2223 setverdict(fail);
2224 }
2225 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
2226 setverdict(fail);
2227 }
2228
2229 f_rtpem_stats_err_check(stats);
2230
2231 setverdict(pass);
2232 }
2233
Harald Welte00a067f2017-09-13 23:27:17 +02002234 control {
2235 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002236 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002237 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002238 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002239 execute(TC_crcx_unsupp_mode());
2240 execute(TC_crcx_early_bidir_mode());
2241 execute(TC_crcx_unsupp_param());
2242 execute(TC_crcx_missing_callid());
2243 execute(TC_crcx_missing_mode());
2244 execute(TC_crcx_unsupp_packet_intv());
2245 execute(TC_crcx_illegal_double_lco());
2246 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002247 execute(TC_crcx_wildcarded());
2248 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002249 execute(TC_mdcx_without_crcx());
2250 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002251 execute(TC_mdcx_wildcarded());
2252 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002253 execute(TC_crcx_and_dlcx_ep_callid_connid());
2254 execute(TC_crcx_and_dlcx_ep_callid());
2255 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002256 execute(TC_crcx_and_dlcx_ep_callid_inval());
2257 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002258 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002259
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002260 execute(TC_crcx_osmux_wildcard());
2261 execute(TC_crcx_osmux_fixed());
2262 execute(TC_crcx_osmux_fixed_twice());
2263 execute(TC_one_crcx_receive_only_osmux());
2264 execute(TC_one_crcx_loopback_osmux());
2265 execute(TC_two_crcx_and_rtp_osmux());
2266 execute(TC_two_crcx_and_rtp_osmux_bidir());
2267 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2268 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2269
Harald Welte33d82162017-12-28 03:21:57 +01002270 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002271
2272 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002273
2274 execute(TC_one_crcx_receive_only_rtp());
2275 execute(TC_one_crcx_loopback_rtp());
Harald Weltebb7523b2018-03-29 08:52:01 +02002276 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002277 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002278 execute(TC_two_crcx_diff_pt_and_rtp());
2279 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002280 execute(TC_two_crcx_mdcx_and_rtp());
2281 execute(TC_two_crcx_and_unsolicited_rtp());
2282 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002283 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002284 execute(TC_amr_oa_bwe_rtp_conversion());
2285 execute(TC_amr_oa_oa_rtp_conversion());
2286 execute(TC_amr_bwe_bwe_rtp_conversion());
Oliver Smith021141e2019-06-25 12:09:01 +02002287
Pau Espin Pedrold19ba6d2020-01-03 20:01:48 +01002288 execute(TC_conn_timeout());
Philipp Maier2609c752020-07-08 12:38:09 +02002289
2290 execute(TC_e1_crcx_and_dlcx_ep());
2291 execute(TC_e1_crcx_with_overlap());
2292 execute(TC_e1_crcx_loopback());
2293
Harald Welte00a067f2017-09-13 23:27:17 +02002294 }
2295}