blob: 8cb24041df6b89e58627f8da8d8e71f5016372e9 [file] [log] [blame]
Harald Welte00a067f2017-09-13 23:27:17 +02001module MGCP_Test {
Harald Weltef07c2862017-11-18 17:16:24 +01002 import from Osmocom_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +02003 import from MGCP_Types all;
Harald Welte4029e8c2017-11-23 22:00:42 +01004 import from MGCP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +08005 import from SDP_Types all;
6 import from MGCP_CodecPort all;
7 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +01008 import from RTP_CodecPort all;
9 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020010 import from RTP_Emulation all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080011 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010012 import from General_Types all;
13 import from Native_Functions all;
14 import from IPCP_Types all;
15 import from IP_Types all;
16 import from Osmocom_VTY_Functions all;
17 import from TELNETasp_PortType all;
Harald Welte00a067f2017-09-13 23:27:17 +020018
Philipp Maierbb7a01c2018-02-01 12:32:57 +010019 const charstring c_mgw_domain := "mgw";
20 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
21
Harald Welte21ba5572017-09-19 17:55:05 +080022 /* any variables declared in the component will be available to
23 * all functions that 'run on' the named component, similar to
24 * class members in C++ */
Harald Welte00a067f2017-09-13 23:27:17 +020025 type component dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080026 port MGCP_CODEC_PT MGCP;
27 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010028 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080029 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010030
Philipp Maier2321ef92018-06-27 17:52:04 +020031 var RTP_Emulation_CT vc_RTPEM[3];
32 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010033
34 port TELNETasp_PT MGWVTY;
Harald Welte00a067f2017-09-13 23:27:17 +020035 };
36
Harald Weltee1e18c52017-09-17 16:23:07 +080037 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
38 var MgcpTransId tid := int2str(g_trans_id);
39 g_trans_id := g_trans_id + 1;
40 return tid;
41 }
42
Harald Welte21ba5572017-09-19 17:55:05 +080043 /* all parameters declared here can be modified / overridden by
44 * the config file in the [MODULE_PARAMETERS] section. If no
45 * config file is used or the file doesn't specify them, the
46 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080047 modulepar {
48 PortNumber mp_local_udp_port := 2727;
49 charstring mp_local_ip := "127.0.0.1";
50 PortNumber mp_remote_udp_port := 2427;
51 charstring mp_remote_ip := "127.0.0.1";
Harald Weltef07c2862017-11-18 17:16:24 +010052 PortNumber mp_local_rtp_port_base := 10000;
Harald Welte3c6ebb92017-09-16 00:56:57 +080053 }
54
Philipp Maier6137c322019-02-20 16:13:41 +010055 private function f_init_vty() runs on dummy_CT {
56 map(self:MGWVTY, system:MGWVTY);
57 f_vty_set_prompts(MGWVTY);
58 f_vty_transceive(MGWVTY, "enable");
59 }
60
Harald Weltebb7523b2018-03-29 08:52:01 +020061 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
62 runs on dummy_CT {
63 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
64 map(comp_ref:RTP, system:RTP);
65 map(comp_ref:RTCP, system:RTCP);
66 comp_ref.start(RTP_Emulation.f_main());
67 }
68
Harald Welte21ba5572017-09-19 17:55:05 +080069 /* initialization function, called by each test case at the
70 * beginning, but 'initialized' variable ensures its body is
71 * only executed once */
Harald Welteedc45c12017-11-18 19:15:05 +010072 private function f_init(template MgcpEndpoint ep := omit) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080073 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +010074 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +020075
Harald Welteedc45c12017-11-18 19:15:05 +010076 if (initialized == false) {
77 initialized := true;
78
79 /* some random number for the initial transaction id */
80 g_trans_id := float2int(rnd()*65535.0);
81 map(self:MGCP, system:MGCP_CODEC_PT);
82 /* connect the MGCP test port using the given
83 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
84 * */
85 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 +020086 if (not ispresent(res.connId)) {
87 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +020088 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +020089 }
Harald Welteedc45c12017-11-18 19:15:05 +010090 g_mgcp_conn_id := res.connId;
91
Harald Weltebb7523b2018-03-29 08:52:01 +020092 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
93 f_rtpem_init(vc_RTPEM[i], i);
94 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
95 }
Harald Welte3c6ebb92017-09-16 00:56:57 +080096 }
Harald Welte3c6ebb92017-09-16 00:56:57 +080097
Harald Welteedc45c12017-11-18 19:15:05 +010098 if (isvalue(ep)) {
99 /* do a DLCX on all connections of the EP */
100 f_dlcx_ignore(valueof(ep));
101 }
Philipp Maier6137c322019-02-20 16:13:41 +0100102
103 f_init_vty();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800104 }
105
Harald Welte00a067f2017-09-13 23:27:17 +0200106 testcase TC_selftest() runs on dummy_CT {
107 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 +0100108 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 +0200109 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
110 "I: 1\n" &
111 "\n" &
112 "v=0\r\n" &
113 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
114 "s=-\r\n" &
115 "c=IN IP4 0.0.0.0\r\n" &
116 "t=0 0\r\n" &
117 "m=audio 0 RTP/AVP 126\r\n" &
118 "a=rtpmap:126 AMR/8000\r\n" &
119 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100120 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 +0200121 "M: sendrecv\r" &
122 "C: 2\r\n" &
123 "I: 1\r\n" &
124 "L: p:20, a:AMR, nt:IN\r\n" &
125 "\n" &
126 "v=0\r\n" &
127 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800128 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200129 "c=IN IP4 0.0.0.0\r\n" &
130 "t=0 0\r\n" &
131 "m=audio 4441 RTP/AVP 99\r\n" &
132 "a=rtpmap:99 AMR/8000\r\n" &
133 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800134 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200135
136 log(c_auep);
137 log(dec_MgcpCommand(c_auep));
138
139 log(c_mdcx3);
140 log(dec_MgcpCommand(c_mdcx3));
141
142 log(c_mdcx3_ret);
143 log(dec_MgcpResponse(c_mdcx3_ret));
144
145 log(c_mdcx4);
146 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800147
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100148 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
149 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 +0800150
151 log(c_crcx510_ret);
152 log(dec_MgcpResponse(c_crcx510_ret));
153 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100154
155 /* We didn't encounter any DTE, so pass the test */
156 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800157 }
158
Harald Weltee636afd2017-09-17 16:24:09 +0800159 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100160 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800161 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
162 * - CRCX with remote session description and without
163 *
164 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100165 * x packetization != 20ms
166 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800167 * x unsupported mode (517)
168 * x bidirectional mode before RemoteConnDesc: 527
169 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100170 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800171 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
172 */
173
Harald Welte21ba5572017-09-19 17:55:05 +0800174 /* build a receive template for receiving a MGCP message. You
175 * pass the MGCP response template in, and it will generate an
176 * MGCP_RecvFrom template that can match the primitives arriving on the
177 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800178 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
179 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100180 connId := g_mgcp_conn_id,
Harald Weltee636afd2017-09-17 16:24:09 +0800181 remName := mp_remote_ip,
182 remPort := mp_remote_udp_port,
183 locName := mp_local_ip,
184 locPort := mp_local_udp_port,
185 msg := { response := resp }
186 }
187 return mrf;
188 }
189
190 /* Send a MGCP request + receive a (matching!) response */
191 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
192 var MgcpMessage msg := { command := valueof(cmd) };
193 resp.line.trans_id := cmd.line.trans_id;
194 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800195 var MGCP_RecvFrom mrf;
196 timer T := 5.0;
197
Harald Welte55015362017-11-18 16:02:42 +0100198 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800199 T.start;
200 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800201 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200202 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
203 setverdict(fail, "Response didn't match template");
204 mtc.stop;
205 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800206 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200207 [] T.timeout {
208 setverdict(fail, "Timeout waiting for response to ", cmd);
209 mtc.stop;
210 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800211 }
212 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800213
214 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
215 return mrf.msg.response;
216 } else {
217 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
218 return r;
219 }
Harald Welte00a067f2017-09-13 23:27:17 +0200220 }
221
Harald Welteba62c8c2017-11-18 18:26:49 +0100222 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
223 var integer i;
224 for (i := 0; i < lengthof(resp.params); i := i + 1) {
225 var MgcpParameter par := resp.params[i];
226 if (par.code == "I") {
227 return str2hex(par.val);
228 }
229 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200230 setverdict(fail, "Could not find conn id for MgcpReponse");
231 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100232 return '00000000'H;
233 }
234
Harald Welte10889c12017-11-18 19:40:31 +0100235 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
236 template MgcpCallId call_id := omit,
237 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100238 var template MgcpCommand cmd;
239 var MgcpResponse resp;
240 var template MgcpResponse rtmpl := {
241 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100242 code := ret_code,
243 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100244 },
245 params := *,
246 sdp := *
247 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100248 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100249 resp := mgcp_transceive_mgw(cmd, rtmpl);
250 }
251
Harald Welte10889c12017-11-18 19:40:31 +0100252 /* Send DLCX and expect OK response */
253 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
254 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100255 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100256 }
257
Harald Welteba62c8c2017-11-18 18:26:49 +0100258 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100259 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100260 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100261 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100262 }
263
Harald Weltebb7523b2018-03-29 08:52:01 +0200264 type record HostPort {
265 charstring hostname,
266 integer portnr optional
267 }
268 type record RtpFlowData {
269 HostPort em, /* emulation side */
270 HostPort mgw, /* mgw side */
271 uint7_t pt,
272 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200273 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100274 RtpemConfig rtp_cfg optional,
275 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200276 }
277
Philipp Maier2321ef92018-06-27 17:52:04 +0200278 /* Create an RTP flow (bidirectional, or receive-only) */
279 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 +0200280 boolean one_phase := true)
281 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200282 var template MgcpCommand cmd;
283 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100284 var SDP_attribute_list attributes;
285
286 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
287 if (isvalue(flow.fmtp)) {
288 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
289 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200290
291 /* bind local RTP emulation socket */
292 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
293
Philipp Maier28bb8292018-07-20 17:09:17 +0200294 /* configure rtp-emulation */
295 if (ispresent(flow.rtp_cfg)) {
296 f_rtpem_configure(pt, flow.rtp_cfg);
297 } else {
298 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
299 rtp_cfg.tx_payload_type := flow.pt
300 f_rtpem_configure(pt, rtp_cfg);
301 }
302
Harald Weltebb7523b2018-03-29 08:52:01 +0200303 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200304 /* Connect flow to MGW using a CRCX that also contains an SDP
305 * part that tells the MGW where we are listening for RTP streams
306 * that come from the MGW. We get a fully working connection in
307 * one go. */
308
309 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200310 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100311 flow.em.portnr, { int2str(flow.pt) }, attributes);
312
Harald Weltebb7523b2018-03-29 08:52:01 +0200313 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
314 flow.mgcp_conn_id := extract_conn_id(resp);
315 /* extract port number from response */
316 flow.mgw.portnr :=
317 resp.sdp.media_list[0].media_field.ports.port_number;
318 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200319 /* Create a half-open connection only. We do not tell the MGW
320 * where it can send RTP streams to us. This means this
321 * connection will only be able to receive but can not send
322 * data back to us. In order to turn the connection in a fully
323 * bi-directional one, a separate MDCX is needed. */
324
325 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200326 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
327 flow.mgcp_conn_id := extract_conn_id(resp);
328 /* extract MGW-side port number from response */
329 flow.mgw.portnr :=
330 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200331 }
332 /* finally, connect the emulation-side RTP socket to the MGW */
333 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
334 }
335
Philipp Maier2321ef92018-06-27 17:52:04 +0200336 /* Modify an existing RTP flow */
337 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
338 runs on dummy_CT {
339 var template MgcpCommand cmd;
340 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100341 var SDP_attribute_list attributes;
342
343 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
344 if (isvalue(flow.fmtp)) {
345 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
346 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200347
348 /* rebind local RTP emulation socket to the new address */
349 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
350
Philipp Maier28bb8292018-07-20 17:09:17 +0200351 /* reconfigure rtp-emulation */
352 if (ispresent(flow.rtp_cfg)) {
353 f_rtpem_configure(pt, flow.rtp_cfg);
354 } else {
355 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
356 rtp_cfg.tx_payload_type := flow.pt
357 f_rtpem_configure(pt, rtp_cfg);
358 }
359
Philipp Maier2321ef92018-06-27 17:52:04 +0200360 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
361 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
362 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100363 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200364 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
365
366 /* extract MGW-side port number from response. (usually this
367 * will not change, but thats is up to the MGW) */
368 flow.mgw.portnr :=
369 resp.sdp.media_list[0].media_field.ports.port_number;
370
371 /* reconnect the emulation-side RTP socket to the MGW */
372 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
373 }
374
375 /* Delete an existing RTP flow */
376 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
377 runs on dummy_CT {
378 var template MgcpCommand cmd;
379 var MgcpResponse resp;
380
381 /* Switch off RTP flow */
382 f_rtpem_mode(pt, RTPEM_MODE_NONE);
383
384 /* Delete connection on MGW (if needed) */
385 if (isvalue(call_id) and isvalue(ep)) {
386 f_sleep(0.1);
387 f_dlcx_ok(valueof(ep), call_id);
388 }
389 }
390
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100391 function f_crcx(charstring ep_prefix) runs on dummy_CT {
392 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800393 var template MgcpCommand cmd;
394 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100395 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800396
Harald Welteedc45c12017-11-18 19:15:05 +0100397 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800398
Harald Welteba62c8c2017-11-18 18:26:49 +0100399 /* create the connection on the MGW */
400 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100401 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100402 extract_conn_id(resp);
403
404 /* clean-up */
405 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100406 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100407
Philipp Maier45635f42018-06-05 17:28:02 +0200408 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
409 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
410 var template MgcpCommand cmd;
411 var MgcpResponse resp;
412 var MgcpCallId call_id := '1234'H;
413
414 f_init(ep);
415
416 /* create the connection on the MGW */
417 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
418 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
419 extract_conn_id(resp);
420
421 /* clean-up */
422 f_dlcx_ok(ep, call_id);
423
424 /* See also OS#2658: Even when we omit the LCO information, we
425 expect the MGW to pick a sane payload type for us. This
426 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200427 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200428 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200429 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200430 }
431
432 /* See also OS#2658: We also expect the MGW to assign a port
433 number to us. */
434 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
435 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200436 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200437 }
438 }
439
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100440 /* test valid CRCX without SDP */
441 testcase TC_crcx() runs on dummy_CT {
442 f_crcx(c_mgw_ep_rtpbridge);
443 setverdict(pass);
444 }
445
Philipp Maier45635f42018-06-05 17:28:02 +0200446 /* test valid CRCX without SDP and LCO */
447 testcase TC_crcx_no_lco() runs on dummy_CT {
448 f_crcx_no_lco(c_mgw_ep_rtpbridge);
449 setverdict(pass);
450 }
451
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100452 /* test valid CRCX without SDP (older method without endpoint prefix) */
453 testcase TC_crcx_noprefix() runs on dummy_CT {
454 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800455 setverdict(pass);
456 }
457
458 /* test CRCX with unsupported mode, expect 517 */
459 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
460 var template MgcpCommand cmd;
461 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100462 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100463 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800464 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
465
Harald Welteedc45c12017-11-18 19:15:05 +0100466 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800467
Harald Welteba62c8c2017-11-18 18:26:49 +0100468 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800469 resp := mgcp_transceive_mgw(cmd, rtmpl);
470 setverdict(pass);
471 }
472
Harald Welte21ba5572017-09-19 17:55:05 +0800473 /* test CRCX with early bi-directional mode, expect 527 as
474 * bi-diretional media can only be established once both local and
475 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800476 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
477 var template MgcpCommand cmd;
478 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100479 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100480 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800481 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
482
Harald Welteedc45c12017-11-18 19:15:05 +0100483 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800484
Harald Welteba62c8c2017-11-18 18:26:49 +0100485 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800486 resp := mgcp_transceive_mgw(cmd, rtmpl);
487 setverdict(pass);
488 }
489
490 /* test CRCX with unsupported Parameters */
491 testcase TC_crcx_unsupp_param() runs on dummy_CT {
492 var template MgcpCommand cmd;
493 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100494 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100495 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800496 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
497
Harald Welteedc45c12017-11-18 19:15:05 +0100498 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800499
Harald Welteba62c8c2017-11-18 18:26:49 +0100500 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100501 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
502 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
503
Harald Weltee636afd2017-09-17 16:24:09 +0800504 resp := mgcp_transceive_mgw(cmd, rtmpl);
505 setverdict(pass);
506 }
507
508 /* test CRCX with missing CallId */
509 testcase TC_crcx_missing_callid() runs on dummy_CT {
510 var template MgcpCommand cmd;
511 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100512 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100513 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800514
Harald Welteedc45c12017-11-18 19:15:05 +0100515 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800516
Harald Welteba62c8c2017-11-18 18:26:49 +0100517 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800518 cmd.params := {
519 t_MgcpParConnMode("recvonly"),
520 t_MgcpParLocConnOpt("p:20")
521 }
522 resp := mgcp_transceive_mgw(cmd, rtmpl);
523 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100524
Harald Weltee636afd2017-09-17 16:24:09 +0800525 }
526
527 /* test CRCX with missing Mode */
528 testcase TC_crcx_missing_mode() runs on dummy_CT {
529 var template MgcpCommand cmd;
530 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100531 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100532 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100533 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800534
Harald Welteedc45c12017-11-18 19:15:05 +0100535 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800536
Harald Welteba62c8c2017-11-18 18:26:49 +0100537 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800538 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100539 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800540 t_MgcpParLocConnOpt("p:20")
541 }
542 resp := mgcp_transceive_mgw(cmd, rtmpl);
543 setverdict(pass);
544 }
545
546 /* test CRCX with unsupported packetization interval */
547 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
548 var template MgcpCommand cmd;
549 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100550 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100551 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100552 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800553
Harald Welteedc45c12017-11-18 19:15:05 +0100554 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800555
Harald Welteba62c8c2017-11-18 18:26:49 +0100556 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100557 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800558 resp := mgcp_transceive_mgw(cmd, rtmpl);
559 setverdict(pass);
560 }
561
562 /* test CRCX with illegal double presence of local connection option */
563 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
564 var template MgcpCommand cmd;
565 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100566 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100567 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800568 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
569
Harald Welteedc45c12017-11-18 19:15:05 +0100570 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800571
Harald Welteba62c8c2017-11-18 18:26:49 +0100572 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100573 /* p:20 is permitted only once and not twice! */
574 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800575 resp := mgcp_transceive_mgw(cmd, rtmpl);
576 setverdict(pass);
577 }
578
579 /* test valid CRCX with valid SDP */
580 testcase TC_crcx_sdp() runs on dummy_CT {
581 var template MgcpCommand cmd;
582 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100583 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100584 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800585
Harald Welteedc45c12017-11-18 19:15:05 +0100586 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800587
Harald Welteba62c8c2017-11-18 18:26:49 +0100588 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800589 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
590 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
591 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100592 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100593
594 /* clean-up */
595 f_dlcx_ok(ep, call_id);
596
Harald Weltee636afd2017-09-17 16:24:09 +0800597 setverdict(pass);
598 }
599
Philipp Maier5e06cee2018-02-01 18:28:08 +0100600 /* test valid wildcarded CRCX */
601 testcase TC_crcx_wildcarded() runs on dummy_CT {
602 var template MgcpCommand cmd;
603 var MgcpResponse resp;
604 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
605 var MgcpCallId call_id := '1234'H;
606 var MgcpEndpoint ep_assigned;
607 f_init();
608
609 /* create the connection on the MGW */
610 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
611 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
612 extract_conn_id(resp);
613
614 /* extract endpoint name we got assigned by the MGW */
615 var MgcpMessage resp_msg := {
616 response := resp
617 }
618 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
619 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200620 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100621 }
622
623 /* clean-up */
624 f_dlcx_ok(ep_assigned, call_id);
625
626 setverdict(pass);
627 }
628
629 /* test valid wildcarded CRCX */
630 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
631 const integer n_endpoints := 32;
632 var integer i;
633 var template MgcpCommand cmd;
634 var MgcpResponse resp;
635 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
636 var MgcpCallId call_id := '1234'H;
637 var MgcpEndpoint ep_assigned[n_endpoints];
638 f_init();
639
640 /* Exhaust all endpoint resources on the virtual trunk */
641 for (i := 0; i < n_endpoints; i := i+1) {
642 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
643 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
644
645 /* Make sure we got a connection id */
646 extract_conn_id(resp);
647
648 var MgcpMessage resp_msg := {
649 response := resp
650 }
651 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
652 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200653 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100654 }
655 }
656
657 /* Try to allocate one more endpoint, which should fail */
658 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
659 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
660 resp := mgcp_transceive_mgw(cmd, rtmpl);
661 setverdict(pass);
662
663 /* clean-up */
664 for (i := 0; i < n_endpoints; i := i+1) {
665 f_dlcx_ok(ep_assigned[i], call_id);
666 }
667 setverdict(pass);
668 }
669
Harald Weltee636afd2017-09-17 16:24:09 +0800670 /* TODO: various SDP related bits */
671
672
673 /* TODO: CRCX with X-Osmux */
674 /* TODO: double CRCX without force_realloc */
675
676 /* TODO: MDCX (various) */
677
678 /* TODO: MDCX without CRCX first */
679 testcase TC_mdcx_without_crcx() runs on dummy_CT {
680 var template MgcpCommand cmd;
681 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100682 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100683 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800684 var template MgcpResponse rtmpl := {
685 line := {
686 /* TODO: accept/enforce better error? */
687 code := "400",
688 string := ?
689 },
690 params:= { },
691 sdp := omit
692 };
693
Harald Welteedc45c12017-11-18 19:15:05 +0100694 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800695
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100696 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800697 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
698 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
699 valueof(ts_SDP_ptime(20)) });
700 resp := mgcp_transceive_mgw(cmd, rtmpl);
701 setverdict(pass);
702 }
703
704 /* DLCX without CRCX first */
705 testcase TC_dlcx_without_crcx() runs on dummy_CT {
706 var template MgcpCommand cmd;
707 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100708 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800709 var template MgcpResponse rtmpl := {
710 line := {
Harald Weltef91edf32017-12-28 14:16:21 +0100711 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +0800712 string := ?
713 },
714 params:= { },
715 sdp := omit
716 };
717
Harald Welteedc45c12017-11-18 19:15:05 +0100718 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800719
Harald Welteedc45c12017-11-18 19:15:05 +0100720 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800721 resp := mgcp_transceive_mgw(cmd, rtmpl);
722 setverdict(pass);
723 }
724
Philipp Maier8a3dc922018-02-02 14:55:12 +0100725 /* test valid wildcarded MDCX */
726 testcase TC_mdcx_wildcarded() runs on dummy_CT {
727 /* Note: A wildcarded MDCX is not allowed, so we expect the
728 * MGW to reject this request */
729 var template MgcpCommand cmd;
730 var MgcpResponse resp;
731 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
732 var MgcpCallId call_id := '1225'H;
733 var template MgcpResponse rtmpl := {
734 line := {
735 /* TODO: accept/enforce better error? */
736 code := "507",
737 string := ?
738 },
739 params:= { },
740 sdp := omit
741 };
742
743 f_init(ep);
744
745 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
746 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
747 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
748 valueof(ts_SDP_ptime(20)) });
749 resp := mgcp_transceive_mgw(cmd, rtmpl);
750 setverdict(pass);
751 }
752
753 /* test valid wildcarded DLCX */
754 testcase TC_dlcx_wildcarded() runs on dummy_CT {
755 /* Note: A wildcarded DLCX is specified, but our MGW does not
756 * support this feature so we expect the MGW to reject the
757 * request */
758 var template MgcpCommand cmd;
759 var MgcpResponse resp;
760 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
761 var template MgcpResponse rtmpl := {
762 line := {
763 code := "507",
764 string := ?
765 },
766 params:= { },
767 sdp := omit
768 };
769
770 f_init(ep);
771
772 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
773 resp := mgcp_transceive_mgw(cmd, rtmpl);
774 setverdict(pass);
775 }
776
Harald Welte79181ff2017-11-18 19:26:11 +0100777 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
778 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
779 var template MgcpCommand cmd;
780 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100781 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +0100782 var MgcpCallId call_id := '51234'H;
783
784 f_init(ep);
785
786 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
787 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
788
789 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
790
791 setverdict(pass);
792 }
793
Harald Welte646ecdb2017-12-28 03:21:57 +0100794 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
795 var template MgcpCommand cmd;
796 var MgcpResponse resp;
797
798 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
799 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
800
801 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
802
803 setverdict(pass);
804 }
805
806 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
807 var MgcpEndpoint ep;
808 var MgcpCallId call_id;
809 var integer ep_nr;
810
811 f_init();
812
813 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100814 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
Harald Welte646ecdb2017-12-28 03:21:57 +0100815 call_id := int2hex(ep_nr, 2) & '1234'H;
816 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
817 }
818 }
819
Harald Welte79181ff2017-11-18 19:26:11 +0100820 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
821 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +0800822 var template MgcpCommand cmd;
823 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100824 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100825 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +0800826
Harald Welteedc45c12017-11-18 19:15:05 +0100827 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800828
Harald Welteba62c8c2017-11-18 18:26:49 +0100829 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100830 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800831
Harald Welteba62c8c2017-11-18 18:26:49 +0100832 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800833
834 setverdict(pass);
835 }
836
Harald Welte79181ff2017-11-18 19:26:11 +0100837 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
838 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
839 var template MgcpCommand cmd;
840 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100841 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100842 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +0100843
844 f_init(ep);
845
846 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
847 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
848
849 f_dlcx_ok(ep);
850
851 setverdict(pass);
852 }
853
854
Harald Welte6d167f82017-11-18 19:41:35 +0100855 /* CRCX + DLCX of valid endpoint but invalid call-id */
856 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
857 var template MgcpCommand cmd;
858 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100859 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100860 var MgcpCallId call_id := '51231'H;
861
862 f_init(ep);
863
864 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
865 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
866
867 f_dlcx(ep, "516", *, 'ffff'H);
868
869 setverdict(pass);
870 }
871
872
873 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
874 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
875 var template MgcpCommand cmd;
876 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100877 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100878 var MgcpCallId call_id := '51230'H;
879
880 f_init(ep);
881
882 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
883 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
884
885 f_dlcx(ep, "515", *, call_id, 'ffff'H);
886
887 setverdict(pass);
888 }
889
890
Harald Weltee636afd2017-09-17 16:24:09 +0800891 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +0100892 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
893 var template MgcpCommand cmd;
894 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100895 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +0100896 var MgcpCallId call_id := '51229'H;
897 var template MgcpResponse rtmpl := {
898 line := {
899 code := "200",
900 string := "OK"
901 },
902 params:= { },
903 sdp := omit
904 };
905
906 f_init(ep);
907
908 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
909 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
910
911 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
912 resp := mgcp_transceive_mgw(cmd, rtmpl);
913 resp := mgcp_transceive_mgw(cmd, rtmpl);
914
915 setverdict(pass);
916 }
917
Harald Weltebb7523b2018-03-29 08:52:01 +0200918 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
919 charstring codec) := {
920 em := {
921 hostname := host_a,
922 portnr := omit
923 },
924 mgw := {
925 hostname := host_b,
926 portnr := omit
927 },
928 pt := pt,
929 codec := codec
930 }
Harald Weltef53f1642017-11-18 19:57:11 +0100931
Harald Weltebb7523b2018-03-29 08:52:01 +0200932 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
933 testcase TC_rtpem_selftest() runs on dummy_CT {
934 var RtpemStats stats[2];
935 var integer local_port := 10000;
936 var integer local_port2 := 20000;
937
938 f_init();
939
940 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
941 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
942
943 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
944 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
945
946 log("=== starting");
947 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
948 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
949
950 f_sleep(5.0);
951
952 log("=== stopping");
953 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
954 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
955 f_sleep(0.5);
956 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
957 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
958
959 stats[0] := f_rtpem_stats_get(RTPEM[0]);
960 stats[1] := f_rtpem_stats_get(RTPEM[1]);
961 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
962 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +0200963 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +0200964 }
965 setverdict(pass);
966 }
967
Philipp Maier2321ef92018-06-27 17:52:04 +0200968 /* Create one half open connection in receive-only mode. The MGW must accept
969 * the packets but must not send any. */
970 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
971 var RtpFlowData flow;
972 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
973 var MgcpCallId call_id := '1225'H;
974 var RtpemStats stats;
975
976 f_init(ep);
977 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
978 flow.em.portnr := 10000;
979 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, false);
980
981 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
982 f_sleep(1.0);
983 f_flow_delete(RTPEM[0], ep, call_id);
984
985 stats := f_rtpem_stats_get(RTPEM[0]);
986
987 if (stats.num_pkts_tx < 40) {
988 setverdict(fail);
989 }
990 if (stats.bytes_payload_tx < 190) {
991 setverdict(fail);
992 }
Philipp Maier36291392018-07-25 09:40:44 +0200993
994 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +0200995
996 setverdict(pass);
997 }
998
999 /* Create one connection in loopback mode, test if the RTP packets are
1000 * actually reflected */
1001 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
1002 var RtpFlowData flow;
1003 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1004 var MgcpCallId call_id := '1225'H;
1005 var RtpemStats stats;
1006
1007 f_init(ep);
1008 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1009 flow.em.portnr := 10000;
1010 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
1011
1012 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1013 f_sleep(1.0);
1014 f_flow_delete(RTPEM[0], ep, call_id);
1015
1016 stats := f_rtpem_stats_get(RTPEM[0]);
1017
1018 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1019 setverdict(fail);
1020 }
1021 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1022 setverdict(fail);
1023 }
Philipp Maier36291392018-07-25 09:40:44 +02001024
1025 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001026
1027 setverdict(pass);
1028 }
1029
Philipp Maier7df85f62018-07-25 10:26:09 +02001030 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1031 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001032 var RtpFlowData flow[2];
1033 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001034 var MgcpResponse resp;
1035 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1036 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001037 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001038
1039 f_init(ep);
1040
1041 /* from us to MGW */
Philipp Maier7df85f62018-07-25 10:26:09 +02001042 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001043 /* bind local RTP emulation sockets */
1044 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001045 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001046
1047 /* from MGW back to us */
Philipp Maier7df85f62018-07-25 10:26:09 +02001048 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001049 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001050 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1051
1052 if (bidir) {
1053 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1054 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1055
1056 /* Note: When we test bidirectional we may
1057 * loose packets during switch off because
1058 * both ends are transmitting and we only
1059 * can switch them off one by one. */
1060 tolerance := 3;
1061 } else {
1062 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1063 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1064 }
1065
1066 f_sleep(1.0);
1067
1068 f_flow_delete(RTPEM[1]);
1069 f_flow_delete(RTPEM[0], ep, call_id);
1070
1071 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1072 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1073 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1074 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001075 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001076 }
1077
Philipp Maier36291392018-07-25 09:40:44 +02001078 f_rtpem_stats_err_check(stats[0]);
1079 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001080
Philipp Maier2321ef92018-06-27 17:52:04 +02001081 setverdict(pass);
1082 }
1083
1084 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1085 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001086 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001087 }
1088
1089 /* create two local RTP emulations; create two connections on MGW EP,
1090 * exchange some data in both directions */
1091 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001092 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1093 }
1094
1095 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1096 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1097 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1098 }
1099
1100 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1101 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1102 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001103 }
1104
1105 /* create two local RTP emulations and pass data in both directions */
1106 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1107 var RtpFlowData flow[2];
1108 var RtpemStats stats[2];
1109 var MgcpResponse resp;
1110 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1111 var MgcpCallId call_id := '1227'H;
1112 var integer num_pkts_tx[2];
1113 var integer temp;
1114
1115 f_init(ep);
1116
1117 /* Create the first connection in receive only mode */
1118 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1119 flow[0].em.portnr := 10000;
1120 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1121
1122 /* Create the second connection. This connection will be also
1123 * in receive only mode */
1124 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1125 flow[1].em.portnr := 20000;
1126 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], false);
1127
1128 /* The first leg starts transmitting */
1129 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1130 f_sleep(0.5);
1131 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1132 if (stats[0].num_pkts_rx_err_disabled != 0) {
1133 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001134 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001135 }
1136 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1137 if (stats[1].num_pkts_rx_err_disabled != 0) {
1138 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001139 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001140 }
1141
1142 /* The second leg starts transmitting a little later */
1143 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1144 f_sleep(1.0);
1145 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1146 if (stats[0].num_pkts_rx_err_disabled != 0) {
1147 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001148 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001149 }
1150 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1151 if (stats[1].num_pkts_rx_err_disabled != 0) {
1152 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001153 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001154 }
1155
1156 /* The first leg will now be switched into bidirectional
1157 * mode, but we do not expect any data comming back yet. */
1158 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1159 f_sleep(0.5);
1160 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1161 if (stats[1].num_pkts_rx_err_disabled != 0) {
1162 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001163 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001164 }
1165 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1166 if (stats[1].num_pkts_rx_err_disabled != 0) {
1167 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001168 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001169 }
1170
1171 /* When the second leg is switched into bidirectional mode
1172 * as well, then the MGW will connect the two together and
1173 * we should see RTP streams passing through from both ends. */
1174 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1175 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1176 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1177 num_pkts_tx[0] := stats[0].num_pkts_tx
1178 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1179 num_pkts_tx[1] := stats[1].num_pkts_tx
1180
1181 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1182 f_sleep(2.0);
1183
1184 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1185 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1186
1187 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1188 if (temp > 3 or temp < -3) {
1189 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001190 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001191 }
1192
1193 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1194 if (temp > 3 or temp < -3) {
1195 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001196 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001197 }
Philipp Maier36291392018-07-25 09:40:44 +02001198
1199 f_rtpem_stats_err_check(stats[0]);
1200 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001201
1202 /* Tear down */
1203 f_flow_delete(RTPEM[0]);
1204 f_flow_delete(RTPEM[1], ep, call_id);
1205 setverdict(pass);
1206 }
1207
1208 /* Test what happens when two RTP streams from different sources target
1209 * a single connection. Is the unsolicited stream properly ignored? */
1210 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1211 var RtpFlowData flow[2];
1212 var RtpemStats stats[2];
1213 var MgcpResponse resp;
1214 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1215 var MgcpCallId call_id := '1234321326'H;
1216 var integer unsolicited_port := 10002;
1217
1218 f_init(ep);
1219
1220 /* from us to MGW */
1221 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1222 /* bind local RTP emulation sockets */
1223 flow[0].em.portnr := 10000;
1224 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1225
1226 /* from MGW back to us */
1227 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1228 flow[1].em.portnr := 20000;
1229 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001230
1231 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1232 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1233
Philipp Maier2321ef92018-06-27 17:52:04 +02001234 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001235
Philipp Maier2321ef92018-06-27 17:52:04 +02001236 /* Start inserting unsolicited RTP packets */
Philipp Maier4de3e372018-06-29 17:15:22 +02001237 f_rtpem_bind(RTPEM[2], mp_local_ip, unsolicited_port);
1238 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001239 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1240
1241 f_sleep(0.5);
1242
Daniel Willmanna069d382018-12-13 13:53:33 +01001243 /* Stop transmitting packets and tear down the flows */
1244 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02001245 f_flow_delete(RTPEM[0]);
1246 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001247
1248 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1249 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1250 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1251 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001252 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001253 }
1254
Philipp Maier36291392018-07-25 09:40:44 +02001255 f_rtpem_stats_err_check(stats[0]);
1256 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02001257
Harald Weltebb7523b2018-03-29 08:52:01 +02001258 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001259 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001260
Philipp Maier2321ef92018-06-27 17:52:04 +02001261 /* Test a handover situation. We first create two connections transmit
1262 * some data bidirectionally. Then we will simulate a handover situation. */
1263 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1264 var RtpFlowData flow[2];
1265 var RtpemStats stats[3];
1266 var MgcpResponse resp;
1267 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1268 var MgcpCallId call_id := '76338'H;
1269 var integer port_old;
1270
1271 f_init(ep);
1272
1273 /* First connection (BTS) */
1274 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1275 /* bind local RTP emulation sockets */
1276 flow[0].em.portnr := 10000;
1277 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1278
1279 /* Second connection (PBX) */
1280 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1281 flow[1].em.portnr := 20000;
1282 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1283
1284 /* Normal rtp flow for one second */
1285 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1286 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1287 f_sleep(1.0);
1288
1289 /* Now switch the flow over to a new port (BTS) */
1290 port_old := flow[0].em.portnr;
1291 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001292 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02001293 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001294 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02001295
1296 /* When handing over a call, the old source may still keep
1297 * transmitting for a while. We simulate this by injecting
1298 * some unsolicited packets on the behalf of the old source,
1299 * (old remote port) */
Philipp Maier4de3e372018-06-29 17:15:22 +02001300 f_rtpem_bind(RTPEM[2], mp_local_ip, port_old);
1301 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001302 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1303 f_sleep(1.0);
1304 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
1305 f_sleep(1.0);
1306
1307 /* Terminate call */
1308 f_flow_delete(RTPEM[0]);
1309 f_flow_delete(RTPEM[1], ep, call_id);
1310
1311 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1312 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1313 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
1314 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001315 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001316 }
1317 stats[2] := f_rtpem_stats_get(RTPEM[2]);
1318 if (stats[2].num_pkts_rx_err_disabled != 0) {
1319 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02001320 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001321 }
1322
Philipp Maier36291392018-07-25 09:40:44 +02001323 f_rtpem_stats_err_check(stats[0]);
1324 f_rtpem_stats_err_check(stats[1]);
1325 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02001326
Philipp Maier2321ef92018-06-27 17:52:04 +02001327 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02001328 }
Harald Weltef53f1642017-11-18 19:57:11 +01001329
Philipp Maier6d4e0942019-02-21 17:35:01 +01001330
1331 /* create two local RTP emulations; create two connections on MGW EP, see if
1332 * exchanged data is converted bwtween ts101318 and rfc5993 */
1333 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
1334 var RtpFlowData flow[2];
1335 var RtpemStats stats[2];
1336 var MgcpResponse resp;
1337 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1338 var MgcpCallId call_id := '1226'H;
1339
1340 f_init(ep);
1341
1342 /* Turn on conversion mode */
1343 f_vty_enter_config(MGWVTY);
1344 f_vty_transceive(MGWVTY, "mgcp");
1345 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
1346
1347 /* from us to MGW */
1348 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1349 /* bind local RTP emulation sockets */
1350 flow[0].em.portnr := 10000;
1351 flow[0].rtp_cfg := c_RtpemDefaultCfg;
1352 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1353 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1354 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1355 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1356
1357 /* from MGW back to us */
1358 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1359 flow[1].em.portnr := 20000;
1360 flow[1].rtp_cfg := c_RtpemDefaultCfg;
1361 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
1362 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1363 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1364 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1365
1366 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1367 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1368
1369 f_sleep(1.0);
1370
1371 f_flow_delete(RTPEM[0]);
1372 f_flow_delete(RTPEM[1], ep, call_id);
1373
1374 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1375 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1376
1377 f_rtpem_stats_err_check(stats[0]);
1378 f_rtpem_stats_err_check(stats[1]);
1379
1380 /* Turn off conversion mode */
1381 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
1382
1383 setverdict(pass);
1384 }
1385
Philipp Maier4f764ce2019-03-07 10:54:10 +01001386 /* create two local RTP emulations; create two connections on MGW EP, see if
1387 * exchanged data is converted between AMR octet-aligned and bandwith
1388 * efficient-mode */
1389 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
1390 var RtpFlowData flow[2];
1391 var RtpemStats stats[2];
1392 var MgcpResponse resp;
1393 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1394 var MgcpCallId call_id := '1226'H;
1395
1396 f_init(ep);
1397
1398 /* from us to MGW */
1399 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1400 /* bind local RTP emulation sockets */
1401 flow[0].em.portnr := 10000;
1402 flow[0].rtp_cfg := c_RtpemDefaultCfg;
1403 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1404 flow[0].rtp_cfg.rx_fixed_payload := pl0;
1405 flow[0].rtp_cfg.tx_fixed_payload := pl0;
1406 flow[0].fmtp := fmtp0;
1407 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1408
1409 /* from MGW back to us */
1410 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1411 flow[1].em.portnr := 20000;
1412 flow[1].rtp_cfg := c_RtpemDefaultCfg;
1413 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
1414 flow[1].rtp_cfg.rx_fixed_payload := pl1;
1415 flow[1].rtp_cfg.tx_fixed_payload := pl1;
1416 flow[1].fmtp := fmtp1;
1417 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1418
1419 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1420 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1421
1422 f_sleep(1.0);
1423
1424 f_flow_delete(RTPEM[0]);
1425 f_flow_delete(RTPEM[1], ep, call_id);
1426
1427 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1428 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1429
1430 f_rtpem_stats_err_check(stats[0]);
1431 f_rtpem_stats_err_check(stats[1]);
1432
1433 setverdict(pass);
1434 }
1435
1436 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
1437 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=1", "octet-align=0");
1438 }
1439
1440 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
1441 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
1442 }
1443
1444 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
1445 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
1446 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01001447
Harald Weltee636afd2017-09-17 16:24:09 +08001448 /* TODO: Double-DLCX (no retransmission) */
1449
1450
1451
1452 /* TODO: AUEP (various) */
1453 /* TODO: RSIP (various) */
1454 /* TODO: RQNT (various) */
1455 /* TODO: EPCF (various) */
1456 /* TODO: AUCX (various) */
1457 /* TODO: invalid verb (various) */
1458
Harald Welte00a067f2017-09-13 23:27:17 +02001459 control {
1460 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08001461 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02001462 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001463 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08001464 execute(TC_crcx_unsupp_mode());
1465 execute(TC_crcx_early_bidir_mode());
1466 execute(TC_crcx_unsupp_param());
1467 execute(TC_crcx_missing_callid());
1468 execute(TC_crcx_missing_mode());
1469 execute(TC_crcx_unsupp_packet_intv());
1470 execute(TC_crcx_illegal_double_lco());
1471 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01001472 execute(TC_crcx_wildcarded());
1473 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08001474 execute(TC_mdcx_without_crcx());
1475 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01001476 execute(TC_mdcx_wildcarded());
1477 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01001478 execute(TC_crcx_and_dlcx_ep_callid_connid());
1479 execute(TC_crcx_and_dlcx_ep_callid());
1480 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01001481 execute(TC_crcx_and_dlcx_ep_callid_inval());
1482 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01001483 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01001484
1485 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02001486
1487 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02001488
1489 execute(TC_one_crcx_receive_only_rtp());
1490 execute(TC_one_crcx_loopback_rtp());
Harald Weltebb7523b2018-03-29 08:52:01 +02001491 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02001492 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02001493 execute(TC_two_crcx_diff_pt_and_rtp());
1494 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02001495 execute(TC_two_crcx_mdcx_and_rtp());
1496 execute(TC_two_crcx_and_unsolicited_rtp());
1497 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01001498 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01001499 execute(TC_amr_oa_bwe_rtp_conversion());
1500 execute(TC_amr_oa_oa_rtp_conversion());
1501 execute(TC_amr_bwe_bwe_rtp_conversion());
Harald Welte00a067f2017-09-13 23:27:17 +02001502 }
1503}