blob: 4969a3f6ae14e2c660cb883e9047a0cd871cf3d0 [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));
154 }
155
Harald Weltee636afd2017-09-17 16:24:09 +0800156 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100157 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800158 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
159 * - CRCX with remote session description and without
160 *
161 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100162 * x packetization != 20ms
163 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800164 * x unsupported mode (517)
165 * x bidirectional mode before RemoteConnDesc: 527
166 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100167 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800168 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
169 */
170
Harald Welte21ba5572017-09-19 17:55:05 +0800171 /* build a receive template for receiving a MGCP message. You
172 * pass the MGCP response template in, and it will generate an
173 * MGCP_RecvFrom template that can match the primitives arriving on the
174 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800175 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
176 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100177 connId := g_mgcp_conn_id,
Harald Weltee636afd2017-09-17 16:24:09 +0800178 remName := mp_remote_ip,
179 remPort := mp_remote_udp_port,
180 locName := mp_local_ip,
181 locPort := mp_local_udp_port,
182 msg := { response := resp }
183 }
184 return mrf;
185 }
186
187 /* Send a MGCP request + receive a (matching!) response */
188 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
189 var MgcpMessage msg := { command := valueof(cmd) };
190 resp.line.trans_id := cmd.line.trans_id;
191 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800192 var MGCP_RecvFrom mrf;
193 timer T := 5.0;
194
Harald Welte55015362017-11-18 16:02:42 +0100195 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800196 T.start;
197 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800198 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200199 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
200 setverdict(fail, "Response didn't match template");
201 mtc.stop;
202 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800203 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200204 [] T.timeout {
205 setverdict(fail, "Timeout waiting for response to ", cmd);
206 mtc.stop;
207 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800208 }
209 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800210
211 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
212 return mrf.msg.response;
213 } else {
214 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
215 return r;
216 }
Harald Welte00a067f2017-09-13 23:27:17 +0200217 }
218
Harald Welteba62c8c2017-11-18 18:26:49 +0100219 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
220 var integer i;
221 for (i := 0; i < lengthof(resp.params); i := i + 1) {
222 var MgcpParameter par := resp.params[i];
223 if (par.code == "I") {
224 return str2hex(par.val);
225 }
226 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200227 setverdict(fail, "Could not find conn id for MgcpReponse");
228 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100229 return '00000000'H;
230 }
231
Harald Welte10889c12017-11-18 19:40:31 +0100232 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
233 template MgcpCallId call_id := omit,
234 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100235 var template MgcpCommand cmd;
236 var MgcpResponse resp;
237 var template MgcpResponse rtmpl := {
238 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100239 code := ret_code,
240 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100241 },
242 params := *,
243 sdp := *
244 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100245 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100246 resp := mgcp_transceive_mgw(cmd, rtmpl);
247 }
248
Harald Welte10889c12017-11-18 19:40:31 +0100249 /* Send DLCX and expect OK response */
250 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
251 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100252 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100253 }
254
Harald Welteba62c8c2017-11-18 18:26:49 +0100255 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100256 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100257 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100258 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100259 }
260
Harald Weltebb7523b2018-03-29 08:52:01 +0200261 type record HostPort {
262 charstring hostname,
263 integer portnr optional
264 }
265 type record RtpFlowData {
266 HostPort em, /* emulation side */
267 HostPort mgw, /* mgw side */
268 uint7_t pt,
269 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200270 MgcpConnectionId mgcp_conn_id optional,
271 RtpemConfig rtp_cfg optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200272 }
273
Philipp Maier2321ef92018-06-27 17:52:04 +0200274 /* Create an RTP flow (bidirectional, or receive-only) */
275 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 +0200276 boolean one_phase := true)
277 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200278 var template MgcpCommand cmd;
279 var MgcpResponse resp;
280
281 /* bind local RTP emulation socket */
282 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
283
Philipp Maier28bb8292018-07-20 17:09:17 +0200284 /* configure rtp-emulation */
285 if (ispresent(flow.rtp_cfg)) {
286 f_rtpem_configure(pt, flow.rtp_cfg);
287 } else {
288 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
289 rtp_cfg.tx_payload_type := flow.pt
290 f_rtpem_configure(pt, rtp_cfg);
291 }
292
Harald Weltebb7523b2018-03-29 08:52:01 +0200293 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200294 /* Connect flow to MGW using a CRCX that also contains an SDP
295 * part that tells the MGW where we are listening for RTP streams
296 * that come from the MGW. We get a fully working connection in
297 * one go. */
298
299 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200300 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Harald Weltebb7523b2018-03-29 08:52:01 +0200301 flow.em.portnr, { int2str(flow.pt) },
302 { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)),
303 valueof(ts_SDP_ptime(20)) });
304 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
305 flow.mgcp_conn_id := extract_conn_id(resp);
306 /* extract port number from response */
307 flow.mgw.portnr :=
308 resp.sdp.media_list[0].media_field.ports.port_number;
309 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200310 /* Create a half-open connection only. We do not tell the MGW
311 * where it can send RTP streams to us. This means this
312 * connection will only be able to receive but can not send
313 * data back to us. In order to turn the connection in a fully
314 * bi-directional one, a separate MDCX is needed. */
315
316 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200317 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
318 flow.mgcp_conn_id := extract_conn_id(resp);
319 /* extract MGW-side port number from response */
320 flow.mgw.portnr :=
321 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200322 }
323 /* finally, connect the emulation-side RTP socket to the MGW */
324 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
325 }
326
Philipp Maier2321ef92018-06-27 17:52:04 +0200327 /* Modify an existing RTP flow */
328 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
329 runs on dummy_CT {
330 var template MgcpCommand cmd;
331 var MgcpResponse resp;
332
333 /* rebind local RTP emulation socket to the new address */
334 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
335
Philipp Maier28bb8292018-07-20 17:09:17 +0200336 /* reconfigure rtp-emulation */
337 if (ispresent(flow.rtp_cfg)) {
338 f_rtpem_configure(pt, flow.rtp_cfg);
339 } else {
340 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
341 rtp_cfg.tx_payload_type := flow.pt
342 f_rtpem_configure(pt, rtp_cfg);
343 }
344
Philipp Maier2321ef92018-06-27 17:52:04 +0200345 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
346 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
347 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
348 flow.em.portnr, { int2str(flow.pt) },
349 { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)),
350 valueof(ts_SDP_ptime(20)) });
351 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
352
353 /* extract MGW-side port number from response. (usually this
354 * will not change, but thats is up to the MGW) */
355 flow.mgw.portnr :=
356 resp.sdp.media_list[0].media_field.ports.port_number;
357
358 /* reconnect the emulation-side RTP socket to the MGW */
359 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
360 }
361
362 /* Delete an existing RTP flow */
363 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
364 runs on dummy_CT {
365 var template MgcpCommand cmd;
366 var MgcpResponse resp;
367
368 /* Switch off RTP flow */
369 f_rtpem_mode(pt, RTPEM_MODE_NONE);
370
371 /* Delete connection on MGW (if needed) */
372 if (isvalue(call_id) and isvalue(ep)) {
373 f_sleep(0.1);
374 f_dlcx_ok(valueof(ep), call_id);
375 }
376 }
377
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100378 function f_crcx(charstring ep_prefix) runs on dummy_CT {
379 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800380 var template MgcpCommand cmd;
381 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100382 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800383
Harald Welteedc45c12017-11-18 19:15:05 +0100384 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800385
Harald Welteba62c8c2017-11-18 18:26:49 +0100386 /* create the connection on the MGW */
387 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100388 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100389 extract_conn_id(resp);
390
391 /* clean-up */
392 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100393 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100394
Philipp Maier45635f42018-06-05 17:28:02 +0200395 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
396 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
397 var template MgcpCommand cmd;
398 var MgcpResponse resp;
399 var MgcpCallId call_id := '1234'H;
400
401 f_init(ep);
402
403 /* create the connection on the MGW */
404 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
405 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
406 extract_conn_id(resp);
407
408 /* clean-up */
409 f_dlcx_ok(ep, call_id);
410
411 /* See also OS#2658: Even when we omit the LCO information, we
412 expect the MGW to pick a sane payload type for us. This
413 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200414 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200415 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200416 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200417 }
418
419 /* See also OS#2658: We also expect the MGW to assign a port
420 number to us. */
421 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
422 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200423 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200424 }
425 }
426
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100427 /* test valid CRCX without SDP */
428 testcase TC_crcx() runs on dummy_CT {
429 f_crcx(c_mgw_ep_rtpbridge);
430 setverdict(pass);
431 }
432
Philipp Maier45635f42018-06-05 17:28:02 +0200433 /* test valid CRCX without SDP and LCO */
434 testcase TC_crcx_no_lco() runs on dummy_CT {
435 f_crcx_no_lco(c_mgw_ep_rtpbridge);
436 setverdict(pass);
437 }
438
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100439 /* test valid CRCX without SDP (older method without endpoint prefix) */
440 testcase TC_crcx_noprefix() runs on dummy_CT {
441 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800442 setverdict(pass);
443 }
444
445 /* test CRCX with unsupported mode, expect 517 */
446 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
447 var template MgcpCommand cmd;
448 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100449 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100450 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800451 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
452
Harald Welteedc45c12017-11-18 19:15:05 +0100453 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800454
Harald Welteba62c8c2017-11-18 18:26:49 +0100455 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800456 resp := mgcp_transceive_mgw(cmd, rtmpl);
457 setverdict(pass);
458 }
459
Harald Welte21ba5572017-09-19 17:55:05 +0800460 /* test CRCX with early bi-directional mode, expect 527 as
461 * bi-diretional media can only be established once both local and
462 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800463 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
464 var template MgcpCommand cmd;
465 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100466 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100467 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800468 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
469
Harald Welteedc45c12017-11-18 19:15:05 +0100470 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800471
Harald Welteba62c8c2017-11-18 18:26:49 +0100472 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800473 resp := mgcp_transceive_mgw(cmd, rtmpl);
474 setverdict(pass);
475 }
476
477 /* test CRCX with unsupported Parameters */
478 testcase TC_crcx_unsupp_param() runs on dummy_CT {
479 var template MgcpCommand cmd;
480 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100481 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100482 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800483 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
484
Harald Welteedc45c12017-11-18 19:15:05 +0100485 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800486
Harald Welteba62c8c2017-11-18 18:26:49 +0100487 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100488 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
489 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
490
Harald Weltee636afd2017-09-17 16:24:09 +0800491 resp := mgcp_transceive_mgw(cmd, rtmpl);
492 setverdict(pass);
493 }
494
495 /* test CRCX with missing CallId */
496 testcase TC_crcx_missing_callid() runs on dummy_CT {
497 var template MgcpCommand cmd;
498 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100499 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100500 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800501
Harald Welteedc45c12017-11-18 19:15:05 +0100502 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800503
Harald Welteba62c8c2017-11-18 18:26:49 +0100504 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800505 cmd.params := {
506 t_MgcpParConnMode("recvonly"),
507 t_MgcpParLocConnOpt("p:20")
508 }
509 resp := mgcp_transceive_mgw(cmd, rtmpl);
510 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100511
Harald Weltee636afd2017-09-17 16:24:09 +0800512 }
513
514 /* test CRCX with missing Mode */
515 testcase TC_crcx_missing_mode() runs on dummy_CT {
516 var template MgcpCommand cmd;
517 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100518 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100519 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100520 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800521
Harald Welteedc45c12017-11-18 19:15:05 +0100522 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800523
Harald Welteba62c8c2017-11-18 18:26:49 +0100524 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800525 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100526 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800527 t_MgcpParLocConnOpt("p:20")
528 }
529 resp := mgcp_transceive_mgw(cmd, rtmpl);
530 setverdict(pass);
531 }
532
533 /* test CRCX with unsupported packetization interval */
534 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
535 var template MgcpCommand cmd;
536 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100537 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100538 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100539 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800540
Harald Welteedc45c12017-11-18 19:15:05 +0100541 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800542
Harald Welteba62c8c2017-11-18 18:26:49 +0100543 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100544 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800545 resp := mgcp_transceive_mgw(cmd, rtmpl);
546 setverdict(pass);
547 }
548
549 /* test CRCX with illegal double presence of local connection option */
550 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
551 var template MgcpCommand cmd;
552 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100553 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100554 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800555 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
556
Harald Welteedc45c12017-11-18 19:15:05 +0100557 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800558
Harald Welteba62c8c2017-11-18 18:26:49 +0100559 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100560 /* p:20 is permitted only once and not twice! */
561 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800562 resp := mgcp_transceive_mgw(cmd, rtmpl);
563 setverdict(pass);
564 }
565
566 /* test valid CRCX with valid SDP */
567 testcase TC_crcx_sdp() runs on dummy_CT {
568 var template MgcpCommand cmd;
569 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100570 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100571 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800572
Harald Welteedc45c12017-11-18 19:15:05 +0100573 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800574
Harald Welteba62c8c2017-11-18 18:26:49 +0100575 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800576 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
577 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
578 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100579 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100580
581 /* clean-up */
582 f_dlcx_ok(ep, call_id);
583
Harald Weltee636afd2017-09-17 16:24:09 +0800584 setverdict(pass);
585 }
586
Philipp Maier5e06cee2018-02-01 18:28:08 +0100587 /* test valid wildcarded CRCX */
588 testcase TC_crcx_wildcarded() runs on dummy_CT {
589 var template MgcpCommand cmd;
590 var MgcpResponse resp;
591 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
592 var MgcpCallId call_id := '1234'H;
593 var MgcpEndpoint ep_assigned;
594 f_init();
595
596 /* create the connection on the MGW */
597 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
598 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
599 extract_conn_id(resp);
600
601 /* extract endpoint name we got assigned by the MGW */
602 var MgcpMessage resp_msg := {
603 response := resp
604 }
605 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
606 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200607 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100608 }
609
610 /* clean-up */
611 f_dlcx_ok(ep_assigned, call_id);
612
613 setverdict(pass);
614 }
615
616 /* test valid wildcarded CRCX */
617 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
618 const integer n_endpoints := 32;
619 var integer i;
620 var template MgcpCommand cmd;
621 var MgcpResponse resp;
622 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
623 var MgcpCallId call_id := '1234'H;
624 var MgcpEndpoint ep_assigned[n_endpoints];
625 f_init();
626
627 /* Exhaust all endpoint resources on the virtual trunk */
628 for (i := 0; i < n_endpoints; i := i+1) {
629 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
630 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
631
632 /* Make sure we got a connection id */
633 extract_conn_id(resp);
634
635 var MgcpMessage resp_msg := {
636 response := resp
637 }
638 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
639 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200640 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100641 }
642 }
643
644 /* Try to allocate one more endpoint, which should fail */
645 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
646 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
647 resp := mgcp_transceive_mgw(cmd, rtmpl);
648 setverdict(pass);
649
650 /* clean-up */
651 for (i := 0; i < n_endpoints; i := i+1) {
652 f_dlcx_ok(ep_assigned[i], call_id);
653 }
654 setverdict(pass);
655 }
656
Harald Weltee636afd2017-09-17 16:24:09 +0800657 /* TODO: various SDP related bits */
658
659
660 /* TODO: CRCX with X-Osmux */
661 /* TODO: double CRCX without force_realloc */
662
663 /* TODO: MDCX (various) */
664
665 /* TODO: MDCX without CRCX first */
666 testcase TC_mdcx_without_crcx() runs on dummy_CT {
667 var template MgcpCommand cmd;
668 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100669 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100670 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800671 var template MgcpResponse rtmpl := {
672 line := {
673 /* TODO: accept/enforce better error? */
674 code := "400",
675 string := ?
676 },
677 params:= { },
678 sdp := omit
679 };
680
Harald Welteedc45c12017-11-18 19:15:05 +0100681 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800682
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100683 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800684 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
685 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
686 valueof(ts_SDP_ptime(20)) });
687 resp := mgcp_transceive_mgw(cmd, rtmpl);
688 setverdict(pass);
689 }
690
691 /* DLCX without CRCX first */
692 testcase TC_dlcx_without_crcx() runs on dummy_CT {
693 var template MgcpCommand cmd;
694 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100695 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800696 var template MgcpResponse rtmpl := {
697 line := {
Harald Weltef91edf32017-12-28 14:16:21 +0100698 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +0800699 string := ?
700 },
701 params:= { },
702 sdp := omit
703 };
704
Harald Welteedc45c12017-11-18 19:15:05 +0100705 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800706
Harald Welteedc45c12017-11-18 19:15:05 +0100707 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800708 resp := mgcp_transceive_mgw(cmd, rtmpl);
709 setverdict(pass);
710 }
711
Philipp Maier8a3dc922018-02-02 14:55:12 +0100712 /* test valid wildcarded MDCX */
713 testcase TC_mdcx_wildcarded() runs on dummy_CT {
714 /* Note: A wildcarded MDCX is not allowed, so we expect the
715 * MGW to reject this request */
716 var template MgcpCommand cmd;
717 var MgcpResponse resp;
718 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
719 var MgcpCallId call_id := '1225'H;
720 var template MgcpResponse rtmpl := {
721 line := {
722 /* TODO: accept/enforce better error? */
723 code := "507",
724 string := ?
725 },
726 params:= { },
727 sdp := omit
728 };
729
730 f_init(ep);
731
732 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
733 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
734 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
735 valueof(ts_SDP_ptime(20)) });
736 resp := mgcp_transceive_mgw(cmd, rtmpl);
737 setverdict(pass);
738 }
739
740 /* test valid wildcarded DLCX */
741 testcase TC_dlcx_wildcarded() runs on dummy_CT {
742 /* Note: A wildcarded DLCX is specified, but our MGW does not
743 * support this feature so we expect the MGW to reject the
744 * request */
745 var template MgcpCommand cmd;
746 var MgcpResponse resp;
747 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
748 var template MgcpResponse rtmpl := {
749 line := {
750 code := "507",
751 string := ?
752 },
753 params:= { },
754 sdp := omit
755 };
756
757 f_init(ep);
758
759 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
760 resp := mgcp_transceive_mgw(cmd, rtmpl);
761 setverdict(pass);
762 }
763
Harald Welte79181ff2017-11-18 19:26:11 +0100764 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
765 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
766 var template MgcpCommand cmd;
767 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100768 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +0100769 var MgcpCallId call_id := '51234'H;
770
771 f_init(ep);
772
773 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
774 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
775
776 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
777
778 setverdict(pass);
779 }
780
Harald Welte646ecdb2017-12-28 03:21:57 +0100781 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
782 var template MgcpCommand cmd;
783 var MgcpResponse resp;
784
785 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
786 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
787
788 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
789
790 setverdict(pass);
791 }
792
793 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
794 var MgcpEndpoint ep;
795 var MgcpCallId call_id;
796 var integer ep_nr;
797
798 f_init();
799
800 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100801 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
Harald Welte646ecdb2017-12-28 03:21:57 +0100802 call_id := int2hex(ep_nr, 2) & '1234'H;
803 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
804 }
805 }
806
Harald Welte79181ff2017-11-18 19:26:11 +0100807 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
808 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +0800809 var template MgcpCommand cmd;
810 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100811 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100812 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +0800813
Harald Welteedc45c12017-11-18 19:15:05 +0100814 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800815
Harald Welteba62c8c2017-11-18 18:26:49 +0100816 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100817 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800818
Harald Welteba62c8c2017-11-18 18:26:49 +0100819 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800820
821 setverdict(pass);
822 }
823
Harald Welte79181ff2017-11-18 19:26:11 +0100824 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
825 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
826 var template MgcpCommand cmd;
827 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100828 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100829 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +0100830
831 f_init(ep);
832
833 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
834 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
835
836 f_dlcx_ok(ep);
837
838 setverdict(pass);
839 }
840
841
Harald Welte6d167f82017-11-18 19:41:35 +0100842 /* CRCX + DLCX of valid endpoint but invalid call-id */
843 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
844 var template MgcpCommand cmd;
845 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100846 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100847 var MgcpCallId call_id := '51231'H;
848
849 f_init(ep);
850
851 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
852 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
853
854 f_dlcx(ep, "516", *, 'ffff'H);
855
856 setverdict(pass);
857 }
858
859
860 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
861 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
862 var template MgcpCommand cmd;
863 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100864 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100865 var MgcpCallId call_id := '51230'H;
866
867 f_init(ep);
868
869 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
870 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
871
872 f_dlcx(ep, "515", *, call_id, 'ffff'H);
873
874 setverdict(pass);
875 }
876
877
Harald Weltee636afd2017-09-17 16:24:09 +0800878 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +0100879 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
880 var template MgcpCommand cmd;
881 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100882 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +0100883 var MgcpCallId call_id := '51229'H;
884 var template MgcpResponse rtmpl := {
885 line := {
886 code := "200",
887 string := "OK"
888 },
889 params:= { },
890 sdp := omit
891 };
892
893 f_init(ep);
894
895 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
896 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
897
898 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
899 resp := mgcp_transceive_mgw(cmd, rtmpl);
900 resp := mgcp_transceive_mgw(cmd, rtmpl);
901
902 setverdict(pass);
903 }
904
Harald Weltebb7523b2018-03-29 08:52:01 +0200905 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
906 charstring codec) := {
907 em := {
908 hostname := host_a,
909 portnr := omit
910 },
911 mgw := {
912 hostname := host_b,
913 portnr := omit
914 },
915 pt := pt,
916 codec := codec
917 }
Harald Weltef53f1642017-11-18 19:57:11 +0100918
Harald Weltebb7523b2018-03-29 08:52:01 +0200919 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
920 testcase TC_rtpem_selftest() runs on dummy_CT {
921 var RtpemStats stats[2];
922 var integer local_port := 10000;
923 var integer local_port2 := 20000;
924
925 f_init();
926
927 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
928 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
929
930 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
931 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
932
933 log("=== starting");
934 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
935 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
936
937 f_sleep(5.0);
938
939 log("=== stopping");
940 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
941 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
942 f_sleep(0.5);
943 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
944 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
945
946 stats[0] := f_rtpem_stats_get(RTPEM[0]);
947 stats[1] := f_rtpem_stats_get(RTPEM[1]);
948 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
949 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +0200950 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +0200951 }
952 setverdict(pass);
953 }
954
Philipp Maier2321ef92018-06-27 17:52:04 +0200955 /* Create one half open connection in receive-only mode. The MGW must accept
956 * the packets but must not send any. */
957 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
958 var RtpFlowData flow;
959 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
960 var MgcpCallId call_id := '1225'H;
961 var RtpemStats stats;
962
963 f_init(ep);
964 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
965 flow.em.portnr := 10000;
966 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, false);
967
968 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
969 f_sleep(1.0);
970 f_flow_delete(RTPEM[0], ep, call_id);
971
972 stats := f_rtpem_stats_get(RTPEM[0]);
973
974 if (stats.num_pkts_tx < 40) {
975 setverdict(fail);
976 }
977 if (stats.bytes_payload_tx < 190) {
978 setverdict(fail);
979 }
Philipp Maier36291392018-07-25 09:40:44 +0200980
981 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +0200982
983 setverdict(pass);
984 }
985
986 /* Create one connection in loopback mode, test if the RTP packets are
987 * actually reflected */
988 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
989 var RtpFlowData flow;
990 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
991 var MgcpCallId call_id := '1225'H;
992 var RtpemStats stats;
993
994 f_init(ep);
995 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
996 flow.em.portnr := 10000;
997 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
998
999 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1000 f_sleep(1.0);
1001 f_flow_delete(RTPEM[0], ep, call_id);
1002
1003 stats := f_rtpem_stats_get(RTPEM[0]);
1004
1005 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1006 setverdict(fail);
1007 }
1008 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1009 setverdict(fail);
1010 }
Philipp Maier36291392018-07-25 09:40:44 +02001011
1012 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001013
1014 setverdict(pass);
1015 }
1016
Philipp Maier7df85f62018-07-25 10:26:09 +02001017 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1018 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001019 var RtpFlowData flow[2];
1020 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001021 var MgcpResponse resp;
1022 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1023 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001024 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001025
1026 f_init(ep);
1027
1028 /* from us to MGW */
Philipp Maier7df85f62018-07-25 10:26:09 +02001029 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001030 /* bind local RTP emulation sockets */
1031 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001032 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001033
1034 /* from MGW back to us */
Philipp Maier7df85f62018-07-25 10:26:09 +02001035 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001036 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001037 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1038
1039 if (bidir) {
1040 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1041 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1042
1043 /* Note: When we test bidirectional we may
1044 * loose packets during switch off because
1045 * both ends are transmitting and we only
1046 * can switch them off one by one. */
1047 tolerance := 3;
1048 } else {
1049 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1050 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1051 }
1052
1053 f_sleep(1.0);
1054
1055 f_flow_delete(RTPEM[1]);
1056 f_flow_delete(RTPEM[0], ep, call_id);
1057
1058 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1059 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1060 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1061 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001062 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001063 }
1064
Philipp Maier36291392018-07-25 09:40:44 +02001065 f_rtpem_stats_err_check(stats[0]);
1066 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001067
Philipp Maier2321ef92018-06-27 17:52:04 +02001068 setverdict(pass);
1069 }
1070
1071 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1072 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001073 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001074 }
1075
1076 /* create two local RTP emulations; create two connections on MGW EP,
1077 * exchange some data in both directions */
1078 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001079 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1080 }
1081
1082 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1083 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1084 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1085 }
1086
1087 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1088 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1089 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001090 }
1091
1092 /* create two local RTP emulations and pass data in both directions */
1093 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1094 var RtpFlowData flow[2];
1095 var RtpemStats stats[2];
1096 var MgcpResponse resp;
1097 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1098 var MgcpCallId call_id := '1227'H;
1099 var integer num_pkts_tx[2];
1100 var integer temp;
1101
1102 f_init(ep);
1103
1104 /* Create the first connection in receive only mode */
1105 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1106 flow[0].em.portnr := 10000;
1107 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1108
1109 /* Create the second connection. This connection will be also
1110 * in receive only mode */
1111 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1112 flow[1].em.portnr := 20000;
1113 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], false);
1114
1115 /* The first leg starts transmitting */
1116 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1117 f_sleep(0.5);
1118 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1119 if (stats[0].num_pkts_rx_err_disabled != 0) {
1120 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001121 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001122 }
1123 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1124 if (stats[1].num_pkts_rx_err_disabled != 0) {
1125 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001126 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001127 }
1128
1129 /* The second leg starts transmitting a little later */
1130 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1131 f_sleep(1.0);
1132 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1133 if (stats[0].num_pkts_rx_err_disabled != 0) {
1134 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001135 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001136 }
1137 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1138 if (stats[1].num_pkts_rx_err_disabled != 0) {
1139 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001140 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001141 }
1142
1143 /* The first leg will now be switched into bidirectional
1144 * mode, but we do not expect any data comming back yet. */
1145 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1146 f_sleep(0.5);
1147 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1148 if (stats[1].num_pkts_rx_err_disabled != 0) {
1149 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001150 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001151 }
1152 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1153 if (stats[1].num_pkts_rx_err_disabled != 0) {
1154 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001155 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001156 }
1157
1158 /* When the second leg is switched into bidirectional mode
1159 * as well, then the MGW will connect the two together and
1160 * we should see RTP streams passing through from both ends. */
1161 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1162 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1163 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1164 num_pkts_tx[0] := stats[0].num_pkts_tx
1165 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1166 num_pkts_tx[1] := stats[1].num_pkts_tx
1167
1168 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1169 f_sleep(2.0);
1170
1171 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1172 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1173
1174 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1175 if (temp > 3 or temp < -3) {
1176 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001177 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001178 }
1179
1180 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1181 if (temp > 3 or temp < -3) {
1182 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001183 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001184 }
Philipp Maier36291392018-07-25 09:40:44 +02001185
1186 f_rtpem_stats_err_check(stats[0]);
1187 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001188
1189 /* Tear down */
1190 f_flow_delete(RTPEM[0]);
1191 f_flow_delete(RTPEM[1], ep, call_id);
1192 setverdict(pass);
1193 }
1194
1195 /* Test what happens when two RTP streams from different sources target
1196 * a single connection. Is the unsolicited stream properly ignored? */
1197 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1198 var RtpFlowData flow[2];
1199 var RtpemStats stats[2];
1200 var MgcpResponse resp;
1201 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1202 var MgcpCallId call_id := '1234321326'H;
1203 var integer unsolicited_port := 10002;
1204
1205 f_init(ep);
1206
1207 /* from us to MGW */
1208 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1209 /* bind local RTP emulation sockets */
1210 flow[0].em.portnr := 10000;
1211 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1212
1213 /* from MGW back to us */
1214 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1215 flow[1].em.portnr := 20000;
1216 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001217
1218 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1219 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1220
Philipp Maier2321ef92018-06-27 17:52:04 +02001221 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001222
Philipp Maier2321ef92018-06-27 17:52:04 +02001223 /* Start inserting unsolicited RTP packets */
Philipp Maier4de3e372018-06-29 17:15:22 +02001224 f_rtpem_bind(RTPEM[2], mp_local_ip, unsolicited_port);
1225 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001226 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1227
1228 f_sleep(0.5);
1229
Daniel Willmanna069d382018-12-13 13:53:33 +01001230 /* Stop transmitting packets and tear down the flows */
1231 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02001232 f_flow_delete(RTPEM[0]);
1233 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001234
1235 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1236 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1237 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1238 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001239 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001240 }
1241
Philipp Maier36291392018-07-25 09:40:44 +02001242 f_rtpem_stats_err_check(stats[0]);
1243 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02001244
Harald Weltebb7523b2018-03-29 08:52:01 +02001245 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001246 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001247
Philipp Maier2321ef92018-06-27 17:52:04 +02001248 /* Test a handover situation. We first create two connections transmit
1249 * some data bidirectionally. Then we will simulate a handover situation. */
1250 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1251 var RtpFlowData flow[2];
1252 var RtpemStats stats[3];
1253 var MgcpResponse resp;
1254 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1255 var MgcpCallId call_id := '76338'H;
1256 var integer port_old;
1257
1258 f_init(ep);
1259
1260 /* First connection (BTS) */
1261 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1262 /* bind local RTP emulation sockets */
1263 flow[0].em.portnr := 10000;
1264 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1265
1266 /* Second connection (PBX) */
1267 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1268 flow[1].em.portnr := 20000;
1269 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1270
1271 /* Normal rtp flow for one second */
1272 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1273 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1274 f_sleep(1.0);
1275
1276 /* Now switch the flow over to a new port (BTS) */
1277 port_old := flow[0].em.portnr;
1278 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001279 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02001280 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001281 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02001282
1283 /* When handing over a call, the old source may still keep
1284 * transmitting for a while. We simulate this by injecting
1285 * some unsolicited packets on the behalf of the old source,
1286 * (old remote port) */
Philipp Maier4de3e372018-06-29 17:15:22 +02001287 f_rtpem_bind(RTPEM[2], mp_local_ip, port_old);
1288 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001289 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1290 f_sleep(1.0);
1291 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
1292 f_sleep(1.0);
1293
1294 /* Terminate call */
1295 f_flow_delete(RTPEM[0]);
1296 f_flow_delete(RTPEM[1], ep, call_id);
1297
1298 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1299 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1300 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
1301 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001302 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001303 }
1304 stats[2] := f_rtpem_stats_get(RTPEM[2]);
1305 if (stats[2].num_pkts_rx_err_disabled != 0) {
1306 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02001307 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001308 }
1309
Philipp Maier36291392018-07-25 09:40:44 +02001310 f_rtpem_stats_err_check(stats[0]);
1311 f_rtpem_stats_err_check(stats[1]);
1312 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02001313
Philipp Maier2321ef92018-06-27 17:52:04 +02001314 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02001315 }
Harald Weltef53f1642017-11-18 19:57:11 +01001316
Philipp Maier6d4e0942019-02-21 17:35:01 +01001317
1318 /* create two local RTP emulations; create two connections on MGW EP, see if
1319 * exchanged data is converted bwtween ts101318 and rfc5993 */
1320 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
1321 var RtpFlowData flow[2];
1322 var RtpemStats stats[2];
1323 var MgcpResponse resp;
1324 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1325 var MgcpCallId call_id := '1226'H;
1326
1327 f_init(ep);
1328
1329 /* Turn on conversion mode */
1330 f_vty_enter_config(MGWVTY);
1331 f_vty_transceive(MGWVTY, "mgcp");
1332 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
1333
1334 /* from us to MGW */
1335 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1336 /* bind local RTP emulation sockets */
1337 flow[0].em.portnr := 10000;
1338 flow[0].rtp_cfg := c_RtpemDefaultCfg;
1339 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1340 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1341 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1342 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1343
1344 /* from MGW back to us */
1345 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1346 flow[1].em.portnr := 20000;
1347 flow[1].rtp_cfg := c_RtpemDefaultCfg;
1348 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
1349 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1350 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1351 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1352
1353 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1354 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1355
1356 f_sleep(1.0);
1357
1358 f_flow_delete(RTPEM[0]);
1359 f_flow_delete(RTPEM[1], ep, call_id);
1360
1361 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1362 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1363
1364 f_rtpem_stats_err_check(stats[0]);
1365 f_rtpem_stats_err_check(stats[1]);
1366
1367 /* Turn off conversion mode */
1368 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
1369
1370 setverdict(pass);
1371 }
1372
1373
Harald Weltee636afd2017-09-17 16:24:09 +08001374 /* TODO: Double-DLCX (no retransmission) */
1375
1376
1377
1378 /* TODO: AUEP (various) */
1379 /* TODO: RSIP (various) */
1380 /* TODO: RQNT (various) */
1381 /* TODO: EPCF (various) */
1382 /* TODO: AUCX (various) */
1383 /* TODO: invalid verb (various) */
1384
Harald Welte00a067f2017-09-13 23:27:17 +02001385 control {
1386 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08001387 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02001388 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001389 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08001390 execute(TC_crcx_unsupp_mode());
1391 execute(TC_crcx_early_bidir_mode());
1392 execute(TC_crcx_unsupp_param());
1393 execute(TC_crcx_missing_callid());
1394 execute(TC_crcx_missing_mode());
1395 execute(TC_crcx_unsupp_packet_intv());
1396 execute(TC_crcx_illegal_double_lco());
1397 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01001398 execute(TC_crcx_wildcarded());
1399 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08001400 execute(TC_mdcx_without_crcx());
1401 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01001402 execute(TC_mdcx_wildcarded());
1403 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01001404 execute(TC_crcx_and_dlcx_ep_callid_connid());
1405 execute(TC_crcx_and_dlcx_ep_callid());
1406 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01001407 execute(TC_crcx_and_dlcx_ep_callid_inval());
1408 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01001409 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01001410
1411 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02001412
1413 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02001414
1415 execute(TC_one_crcx_receive_only_rtp());
1416 execute(TC_one_crcx_loopback_rtp());
Harald Weltebb7523b2018-03-29 08:52:01 +02001417 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02001418 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02001419 execute(TC_two_crcx_diff_pt_and_rtp());
1420 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02001421 execute(TC_two_crcx_mdcx_and_rtp());
1422 execute(TC_two_crcx_and_unsolicited_rtp());
1423 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01001424 execute(TC_ts101318_rfc5993_rtp_conversion());
Harald Welte00a067f2017-09-13 23:27:17 +02001425 }
1426}