blob: 82ed608bcf10efbf32b496bd60925e8872ac2b60 [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;
Harald Welte00a067f2017-09-13 23:27:17 +020012
Philipp Maierbb7a01c2018-02-01 12:32:57 +010013 const charstring c_mgw_domain := "mgw";
14 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
15
Harald Welte21ba5572017-09-19 17:55:05 +080016 /* any variables declared in the component will be available to
17 * all functions that 'run on' the named component, similar to
18 * class members in C++ */
Harald Welte00a067f2017-09-13 23:27:17 +020019 type component dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080020 port MGCP_CODEC_PT MGCP;
21 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010022 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080023 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010024
Philipp Maier2321ef92018-06-27 17:52:04 +020025 var RTP_Emulation_CT vc_RTPEM[3];
26 port RTPEM_CTRL_PT RTPEM[3];
Harald Welte00a067f2017-09-13 23:27:17 +020027 };
28
Harald Weltee1e18c52017-09-17 16:23:07 +080029 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
30 var MgcpTransId tid := int2str(g_trans_id);
31 g_trans_id := g_trans_id + 1;
32 return tid;
33 }
34
Harald Welte21ba5572017-09-19 17:55:05 +080035 /* all parameters declared here can be modified / overridden by
36 * the config file in the [MODULE_PARAMETERS] section. If no
37 * config file is used or the file doesn't specify them, the
38 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080039 modulepar {
40 PortNumber mp_local_udp_port := 2727;
41 charstring mp_local_ip := "127.0.0.1";
42 PortNumber mp_remote_udp_port := 2427;
43 charstring mp_remote_ip := "127.0.0.1";
Harald Weltef07c2862017-11-18 17:16:24 +010044 PortNumber mp_local_rtp_port_base := 10000;
Harald Welte3c6ebb92017-09-16 00:56:57 +080045 }
46
Harald Weltebb7523b2018-03-29 08:52:01 +020047 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
48 runs on dummy_CT {
49 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
50 map(comp_ref:RTP, system:RTP);
51 map(comp_ref:RTCP, system:RTCP);
52 comp_ref.start(RTP_Emulation.f_main());
53 }
54
Harald Welte21ba5572017-09-19 17:55:05 +080055 /* initialization function, called by each test case at the
56 * beginning, but 'initialized' variable ensures its body is
57 * only executed once */
Harald Welteedc45c12017-11-18 19:15:05 +010058 private function f_init(template MgcpEndpoint ep := omit) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080059 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +010060 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +020061
Harald Welteedc45c12017-11-18 19:15:05 +010062 if (initialized == false) {
63 initialized := true;
64
65 /* some random number for the initial transaction id */
66 g_trans_id := float2int(rnd()*65535.0);
67 map(self:MGCP, system:MGCP_CODEC_PT);
68 /* connect the MGCP test port using the given
69 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
70 * */
71 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 +020072 if (not ispresent(res.connId)) {
73 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +020074 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +020075 }
Harald Welteedc45c12017-11-18 19:15:05 +010076 g_mgcp_conn_id := res.connId;
77
Harald Weltebb7523b2018-03-29 08:52:01 +020078 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
79 f_rtpem_init(vc_RTPEM[i], i);
80 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
81 }
Harald Welte3c6ebb92017-09-16 00:56:57 +080082 }
Harald Welte3c6ebb92017-09-16 00:56:57 +080083
Harald Welteedc45c12017-11-18 19:15:05 +010084 if (isvalue(ep)) {
85 /* do a DLCX on all connections of the EP */
86 f_dlcx_ignore(valueof(ep));
87 }
Harald Welte3c6ebb92017-09-16 00:56:57 +080088 }
89
Harald Welte00a067f2017-09-13 23:27:17 +020090 testcase TC_selftest() runs on dummy_CT {
91 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 +010092 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 +020093 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
94 "I: 1\n" &
95 "\n" &
96 "v=0\r\n" &
97 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
98 "s=-\r\n" &
99 "c=IN IP4 0.0.0.0\r\n" &
100 "t=0 0\r\n" &
101 "m=audio 0 RTP/AVP 126\r\n" &
102 "a=rtpmap:126 AMR/8000\r\n" &
103 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100104 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 +0200105 "M: sendrecv\r" &
106 "C: 2\r\n" &
107 "I: 1\r\n" &
108 "L: p:20, a:AMR, nt:IN\r\n" &
109 "\n" &
110 "v=0\r\n" &
111 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800112 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200113 "c=IN IP4 0.0.0.0\r\n" &
114 "t=0 0\r\n" &
115 "m=audio 4441 RTP/AVP 99\r\n" &
116 "a=rtpmap:99 AMR/8000\r\n" &
117 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800118 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200119
120 log(c_auep);
121 log(dec_MgcpCommand(c_auep));
122
123 log(c_mdcx3);
124 log(dec_MgcpCommand(c_mdcx3));
125
126 log(c_mdcx3_ret);
127 log(dec_MgcpResponse(c_mdcx3_ret));
128
129 log(c_mdcx4);
130 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800131
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100132 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
133 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 +0800134
135 log(c_crcx510_ret);
136 log(dec_MgcpResponse(c_crcx510_ret));
137 log(dec_MgcpMessage(c_crcx510_ret));
138 }
139
Harald Weltee636afd2017-09-17 16:24:09 +0800140 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100141 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800142 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
143 * - CRCX with remote session description and without
144 *
145 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100146 * x packetization != 20ms
147 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800148 * x unsupported mode (517)
149 * x bidirectional mode before RemoteConnDesc: 527
150 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100151 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800152 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
153 */
154
Harald Welte21ba5572017-09-19 17:55:05 +0800155 /* build a receive template for receiving a MGCP message. You
156 * pass the MGCP response template in, and it will generate an
157 * MGCP_RecvFrom template that can match the primitives arriving on the
158 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800159 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
160 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100161 connId := g_mgcp_conn_id,
Harald Weltee636afd2017-09-17 16:24:09 +0800162 remName := mp_remote_ip,
163 remPort := mp_remote_udp_port,
164 locName := mp_local_ip,
165 locPort := mp_local_udp_port,
166 msg := { response := resp }
167 }
168 return mrf;
169 }
170
171 /* Send a MGCP request + receive a (matching!) response */
172 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
173 var MgcpMessage msg := { command := valueof(cmd) };
174 resp.line.trans_id := cmd.line.trans_id;
175 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800176 var MGCP_RecvFrom mrf;
177 timer T := 5.0;
178
Harald Welte55015362017-11-18 16:02:42 +0100179 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800180 T.start;
181 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800182 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200183 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
184 setverdict(fail, "Response didn't match template");
185 mtc.stop;
186 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800187 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200188 [] T.timeout {
189 setverdict(fail, "Timeout waiting for response to ", cmd);
190 mtc.stop;
191 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800192 }
193 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800194
195 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
196 return mrf.msg.response;
197 } else {
198 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
199 return r;
200 }
Harald Welte00a067f2017-09-13 23:27:17 +0200201 }
202
Harald Welteba62c8c2017-11-18 18:26:49 +0100203 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
204 var integer i;
205 for (i := 0; i < lengthof(resp.params); i := i + 1) {
206 var MgcpParameter par := resp.params[i];
207 if (par.code == "I") {
208 return str2hex(par.val);
209 }
210 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200211 setverdict(fail, "Could not find conn id for MgcpReponse");
212 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100213 return '00000000'H;
214 }
215
Harald Welte10889c12017-11-18 19:40:31 +0100216 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
217 template MgcpCallId call_id := omit,
218 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100219 var template MgcpCommand cmd;
220 var MgcpResponse resp;
221 var template MgcpResponse rtmpl := {
222 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100223 code := ret_code,
224 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100225 },
226 params := *,
227 sdp := *
228 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100229 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100230 resp := mgcp_transceive_mgw(cmd, rtmpl);
231 }
232
Harald Welte10889c12017-11-18 19:40:31 +0100233 /* Send DLCX and expect OK response */
234 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
235 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100236 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100237 }
238
Harald Welteba62c8c2017-11-18 18:26:49 +0100239 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100240 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100241 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100242 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100243 }
244
Harald Weltebb7523b2018-03-29 08:52:01 +0200245 type record HostPort {
246 charstring hostname,
247 integer portnr optional
248 }
249 type record RtpFlowData {
250 HostPort em, /* emulation side */
251 HostPort mgw, /* mgw side */
252 uint7_t pt,
253 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200254 MgcpConnectionId mgcp_conn_id optional,
255 RtpemConfig rtp_cfg optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200256 }
257
Philipp Maier2321ef92018-06-27 17:52:04 +0200258 /* Create an RTP flow (bidirectional, or receive-only) */
259 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 +0200260 boolean one_phase := true)
261 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200262 var template MgcpCommand cmd;
263 var MgcpResponse resp;
264
265 /* bind local RTP emulation socket */
266 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
267
Philipp Maier28bb8292018-07-20 17:09:17 +0200268 /* configure rtp-emulation */
269 if (ispresent(flow.rtp_cfg)) {
270 f_rtpem_configure(pt, flow.rtp_cfg);
271 } else {
272 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
273 rtp_cfg.tx_payload_type := flow.pt
274 f_rtpem_configure(pt, rtp_cfg);
275 }
276
Harald Weltebb7523b2018-03-29 08:52:01 +0200277 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200278 /* Connect flow to MGW using a CRCX that also contains an SDP
279 * part that tells the MGW where we are listening for RTP streams
280 * that come from the MGW. We get a fully working connection in
281 * one go. */
282
283 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200284 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Harald Weltebb7523b2018-03-29 08:52:01 +0200285 flow.em.portnr, { int2str(flow.pt) },
286 { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)),
287 valueof(ts_SDP_ptime(20)) });
288 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
289 flow.mgcp_conn_id := extract_conn_id(resp);
290 /* extract port number from response */
291 flow.mgw.portnr :=
292 resp.sdp.media_list[0].media_field.ports.port_number;
293 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200294 /* Create a half-open connection only. We do not tell the MGW
295 * where it can send RTP streams to us. This means this
296 * connection will only be able to receive but can not send
297 * data back to us. In order to turn the connection in a fully
298 * bi-directional one, a separate MDCX is needed. */
299
300 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200301 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
302 flow.mgcp_conn_id := extract_conn_id(resp);
303 /* extract MGW-side port number from response */
304 flow.mgw.portnr :=
305 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200306 }
307 /* finally, connect the emulation-side RTP socket to the MGW */
308 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
309 }
310
Philipp Maier2321ef92018-06-27 17:52:04 +0200311 /* Modify an existing RTP flow */
312 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
313 runs on dummy_CT {
314 var template MgcpCommand cmd;
315 var MgcpResponse resp;
316
317 /* rebind local RTP emulation socket to the new address */
318 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
319
Philipp Maier28bb8292018-07-20 17:09:17 +0200320 /* reconfigure rtp-emulation */
321 if (ispresent(flow.rtp_cfg)) {
322 f_rtpem_configure(pt, flow.rtp_cfg);
323 } else {
324 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
325 rtp_cfg.tx_payload_type := flow.pt
326 f_rtpem_configure(pt, rtp_cfg);
327 }
328
Philipp Maier2321ef92018-06-27 17:52:04 +0200329 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
330 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
331 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
332 flow.em.portnr, { int2str(flow.pt) },
333 { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)),
334 valueof(ts_SDP_ptime(20)) });
335 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
336
337 /* extract MGW-side port number from response. (usually this
338 * will not change, but thats is up to the MGW) */
339 flow.mgw.portnr :=
340 resp.sdp.media_list[0].media_field.ports.port_number;
341
342 /* reconnect the emulation-side RTP socket to the MGW */
343 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
344 }
345
346 /* Delete an existing RTP flow */
347 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
348 runs on dummy_CT {
349 var template MgcpCommand cmd;
350 var MgcpResponse resp;
351
352 /* Switch off RTP flow */
353 f_rtpem_mode(pt, RTPEM_MODE_NONE);
354
355 /* Delete connection on MGW (if needed) */
356 if (isvalue(call_id) and isvalue(ep)) {
357 f_sleep(0.1);
358 f_dlcx_ok(valueof(ep), call_id);
359 }
360 }
361
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100362 function f_crcx(charstring ep_prefix) runs on dummy_CT {
363 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800364 var template MgcpCommand cmd;
365 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100366 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800367
Harald Welteedc45c12017-11-18 19:15:05 +0100368 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800369
Harald Welteba62c8c2017-11-18 18:26:49 +0100370 /* create the connection on the MGW */
371 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100372 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100373 extract_conn_id(resp);
374
375 /* clean-up */
376 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100377 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100378
Philipp Maier45635f42018-06-05 17:28:02 +0200379 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
380 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
381 var template MgcpCommand cmd;
382 var MgcpResponse resp;
383 var MgcpCallId call_id := '1234'H;
384
385 f_init(ep);
386
387 /* create the connection on the MGW */
388 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
389 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
390 extract_conn_id(resp);
391
392 /* clean-up */
393 f_dlcx_ok(ep, call_id);
394
395 /* See also OS#2658: Even when we omit the LCO information, we
396 expect the MGW to pick a sane payload type for us. This
397 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200398 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200399 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200400 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200401 }
402
403 /* See also OS#2658: We also expect the MGW to assign a port
404 number to us. */
405 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
406 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200407 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200408 }
409 }
410
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100411 /* test valid CRCX without SDP */
412 testcase TC_crcx() runs on dummy_CT {
413 f_crcx(c_mgw_ep_rtpbridge);
414 setverdict(pass);
415 }
416
Philipp Maier45635f42018-06-05 17:28:02 +0200417 /* test valid CRCX without SDP and LCO */
418 testcase TC_crcx_no_lco() runs on dummy_CT {
419 f_crcx_no_lco(c_mgw_ep_rtpbridge);
420 setverdict(pass);
421 }
422
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100423 /* test valid CRCX without SDP (older method without endpoint prefix) */
424 testcase TC_crcx_noprefix() runs on dummy_CT {
425 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800426 setverdict(pass);
427 }
428
429 /* test CRCX with unsupported mode, expect 517 */
430 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
431 var template MgcpCommand cmd;
432 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100433 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100434 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800435 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
436
Harald Welteedc45c12017-11-18 19:15:05 +0100437 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800438
Harald Welteba62c8c2017-11-18 18:26:49 +0100439 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800440 resp := mgcp_transceive_mgw(cmd, rtmpl);
441 setverdict(pass);
442 }
443
Harald Welte21ba5572017-09-19 17:55:05 +0800444 /* test CRCX with early bi-directional mode, expect 527 as
445 * bi-diretional media can only be established once both local and
446 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800447 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
448 var template MgcpCommand cmd;
449 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100450 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100451 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800452 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
453
Harald Welteedc45c12017-11-18 19:15:05 +0100454 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800455
Harald Welteba62c8c2017-11-18 18:26:49 +0100456 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800457 resp := mgcp_transceive_mgw(cmd, rtmpl);
458 setverdict(pass);
459 }
460
461 /* test CRCX with unsupported Parameters */
462 testcase TC_crcx_unsupp_param() runs on dummy_CT {
463 var template MgcpCommand cmd;
464 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100465 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100466 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800467 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
468
Harald Welteedc45c12017-11-18 19:15:05 +0100469 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800470
Harald Welteba62c8c2017-11-18 18:26:49 +0100471 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100472 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
473 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
474
Harald Weltee636afd2017-09-17 16:24:09 +0800475 resp := mgcp_transceive_mgw(cmd, rtmpl);
476 setverdict(pass);
477 }
478
479 /* test CRCX with missing CallId */
480 testcase TC_crcx_missing_callid() runs on dummy_CT {
481 var template MgcpCommand cmd;
482 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100483 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100484 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800485
Harald Welteedc45c12017-11-18 19:15:05 +0100486 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800487
Harald Welteba62c8c2017-11-18 18:26:49 +0100488 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800489 cmd.params := {
490 t_MgcpParConnMode("recvonly"),
491 t_MgcpParLocConnOpt("p:20")
492 }
493 resp := mgcp_transceive_mgw(cmd, rtmpl);
494 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100495
Harald Weltee636afd2017-09-17 16:24:09 +0800496 }
497
498 /* test CRCX with missing Mode */
499 testcase TC_crcx_missing_mode() runs on dummy_CT {
500 var template MgcpCommand cmd;
501 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100502 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100503 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100504 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800505
Harald Welteedc45c12017-11-18 19:15:05 +0100506 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800507
Harald Welteba62c8c2017-11-18 18:26:49 +0100508 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800509 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100510 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800511 t_MgcpParLocConnOpt("p:20")
512 }
513 resp := mgcp_transceive_mgw(cmd, rtmpl);
514 setverdict(pass);
515 }
516
517 /* test CRCX with unsupported packetization interval */
518 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
519 var template MgcpCommand cmd;
520 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100521 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100522 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100523 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800524
Harald Welteedc45c12017-11-18 19:15:05 +0100525 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800526
Harald Welteba62c8c2017-11-18 18:26:49 +0100527 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100528 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800529 resp := mgcp_transceive_mgw(cmd, rtmpl);
530 setverdict(pass);
531 }
532
533 /* test CRCX with illegal double presence of local connection option */
534 testcase TC_crcx_illegal_double_lco() 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 := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800539 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
540
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 /* p:20 is permitted only once and not twice! */
545 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800546 resp := mgcp_transceive_mgw(cmd, rtmpl);
547 setverdict(pass);
548 }
549
550 /* test valid CRCX with valid SDP */
551 testcase TC_crcx_sdp() runs on dummy_CT {
552 var template MgcpCommand cmd;
553 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100554 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100555 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800556
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, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800560 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
561 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
562 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100563 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100564
565 /* clean-up */
566 f_dlcx_ok(ep, call_id);
567
Harald Weltee636afd2017-09-17 16:24:09 +0800568 setverdict(pass);
569 }
570
Philipp Maier5e06cee2018-02-01 18:28:08 +0100571 /* test valid wildcarded CRCX */
572 testcase TC_crcx_wildcarded() runs on dummy_CT {
573 var template MgcpCommand cmd;
574 var MgcpResponse resp;
575 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
576 var MgcpCallId call_id := '1234'H;
577 var MgcpEndpoint ep_assigned;
578 f_init();
579
580 /* create the connection on the MGW */
581 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
582 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
583 extract_conn_id(resp);
584
585 /* extract endpoint name we got assigned by the MGW */
586 var MgcpMessage resp_msg := {
587 response := resp
588 }
589 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
590 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200591 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100592 }
593
594 /* clean-up */
595 f_dlcx_ok(ep_assigned, call_id);
596
597 setverdict(pass);
598 }
599
600 /* test valid wildcarded CRCX */
601 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
602 const integer n_endpoints := 32;
603 var integer i;
604 var template MgcpCommand cmd;
605 var MgcpResponse resp;
606 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
607 var MgcpCallId call_id := '1234'H;
608 var MgcpEndpoint ep_assigned[n_endpoints];
609 f_init();
610
611 /* Exhaust all endpoint resources on the virtual trunk */
612 for (i := 0; i < n_endpoints; i := i+1) {
613 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
614 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
615
616 /* Make sure we got a connection id */
617 extract_conn_id(resp);
618
619 var MgcpMessage resp_msg := {
620 response := resp
621 }
622 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
623 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200624 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100625 }
626 }
627
628 /* Try to allocate one more endpoint, which should fail */
629 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
630 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
631 resp := mgcp_transceive_mgw(cmd, rtmpl);
632 setverdict(pass);
633
634 /* clean-up */
635 for (i := 0; i < n_endpoints; i := i+1) {
636 f_dlcx_ok(ep_assigned[i], call_id);
637 }
638 setverdict(pass);
639 }
640
Harald Weltee636afd2017-09-17 16:24:09 +0800641 /* TODO: various SDP related bits */
642
643
644 /* TODO: CRCX with X-Osmux */
645 /* TODO: double CRCX without force_realloc */
646
647 /* TODO: MDCX (various) */
648
649 /* TODO: MDCX without CRCX first */
650 testcase TC_mdcx_without_crcx() runs on dummy_CT {
651 var template MgcpCommand cmd;
652 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100653 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100654 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800655 var template MgcpResponse rtmpl := {
656 line := {
657 /* TODO: accept/enforce better error? */
658 code := "400",
659 string := ?
660 },
661 params:= { },
662 sdp := omit
663 };
664
Harald Welteedc45c12017-11-18 19:15:05 +0100665 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800666
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100667 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800668 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
669 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
670 valueof(ts_SDP_ptime(20)) });
671 resp := mgcp_transceive_mgw(cmd, rtmpl);
672 setverdict(pass);
673 }
674
675 /* DLCX without CRCX first */
676 testcase TC_dlcx_without_crcx() runs on dummy_CT {
677 var template MgcpCommand cmd;
678 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100679 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800680 var template MgcpResponse rtmpl := {
681 line := {
Harald Weltef91edf32017-12-28 14:16:21 +0100682 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +0800683 string := ?
684 },
685 params:= { },
686 sdp := omit
687 };
688
Harald Welteedc45c12017-11-18 19:15:05 +0100689 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800690
Harald Welteedc45c12017-11-18 19:15:05 +0100691 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800692 resp := mgcp_transceive_mgw(cmd, rtmpl);
693 setverdict(pass);
694 }
695
Philipp Maier8a3dc922018-02-02 14:55:12 +0100696 /* test valid wildcarded MDCX */
697 testcase TC_mdcx_wildcarded() runs on dummy_CT {
698 /* Note: A wildcarded MDCX is not allowed, so we expect the
699 * MGW to reject this request */
700 var template MgcpCommand cmd;
701 var MgcpResponse resp;
702 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
703 var MgcpCallId call_id := '1225'H;
704 var template MgcpResponse rtmpl := {
705 line := {
706 /* TODO: accept/enforce better error? */
707 code := "507",
708 string := ?
709 },
710 params:= { },
711 sdp := omit
712 };
713
714 f_init(ep);
715
716 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
717 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
718 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
719 valueof(ts_SDP_ptime(20)) });
720 resp := mgcp_transceive_mgw(cmd, rtmpl);
721 setverdict(pass);
722 }
723
724 /* test valid wildcarded DLCX */
725 testcase TC_dlcx_wildcarded() runs on dummy_CT {
726 /* Note: A wildcarded DLCX is specified, but our MGW does not
727 * support this feature so we expect the MGW to reject the
728 * request */
729 var template MgcpCommand cmd;
730 var MgcpResponse resp;
731 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
732 var template MgcpResponse rtmpl := {
733 line := {
734 code := "507",
735 string := ?
736 },
737 params:= { },
738 sdp := omit
739 };
740
741 f_init(ep);
742
743 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
744 resp := mgcp_transceive_mgw(cmd, rtmpl);
745 setverdict(pass);
746 }
747
Harald Welte79181ff2017-11-18 19:26:11 +0100748 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
749 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
750 var template MgcpCommand cmd;
751 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100752 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +0100753 var MgcpCallId call_id := '51234'H;
754
755 f_init(ep);
756
757 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
758 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
759
760 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
761
762 setverdict(pass);
763 }
764
Harald Welte646ecdb2017-12-28 03:21:57 +0100765 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
766 var template MgcpCommand cmd;
767 var MgcpResponse resp;
768
769 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
770 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
771
772 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
773
774 setverdict(pass);
775 }
776
777 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
778 var MgcpEndpoint ep;
779 var MgcpCallId call_id;
780 var integer ep_nr;
781
782 f_init();
783
784 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100785 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
Harald Welte646ecdb2017-12-28 03:21:57 +0100786 call_id := int2hex(ep_nr, 2) & '1234'H;
787 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
788 }
789 }
790
Harald Welte79181ff2017-11-18 19:26:11 +0100791 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
792 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +0800793 var template MgcpCommand cmd;
794 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100795 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100796 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +0800797
Harald Welteedc45c12017-11-18 19:15:05 +0100798 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800799
Harald Welteba62c8c2017-11-18 18:26:49 +0100800 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100801 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800802
Harald Welteba62c8c2017-11-18 18:26:49 +0100803 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800804
805 setverdict(pass);
806 }
807
Harald Welte79181ff2017-11-18 19:26:11 +0100808 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
809 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
810 var template MgcpCommand cmd;
811 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100812 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100813 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +0100814
815 f_init(ep);
816
817 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
818 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
819
820 f_dlcx_ok(ep);
821
822 setverdict(pass);
823 }
824
825
Harald Welte6d167f82017-11-18 19:41:35 +0100826 /* CRCX + DLCX of valid endpoint but invalid call-id */
827 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
828 var template MgcpCommand cmd;
829 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100830 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100831 var MgcpCallId call_id := '51231'H;
832
833 f_init(ep);
834
835 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
836 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
837
838 f_dlcx(ep, "516", *, 'ffff'H);
839
840 setverdict(pass);
841 }
842
843
844 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
845 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
846 var template MgcpCommand cmd;
847 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100848 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100849 var MgcpCallId call_id := '51230'H;
850
851 f_init(ep);
852
853 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
854 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
855
856 f_dlcx(ep, "515", *, call_id, 'ffff'H);
857
858 setverdict(pass);
859 }
860
861
Harald Weltee636afd2017-09-17 16:24:09 +0800862 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +0100863 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
864 var template MgcpCommand cmd;
865 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100866 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +0100867 var MgcpCallId call_id := '51229'H;
868 var template MgcpResponse rtmpl := {
869 line := {
870 code := "200",
871 string := "OK"
872 },
873 params:= { },
874 sdp := omit
875 };
876
877 f_init(ep);
878
879 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
880 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
881
882 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
883 resp := mgcp_transceive_mgw(cmd, rtmpl);
884 resp := mgcp_transceive_mgw(cmd, rtmpl);
885
886 setverdict(pass);
887 }
888
Harald Weltebb7523b2018-03-29 08:52:01 +0200889 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
890 charstring codec) := {
891 em := {
892 hostname := host_a,
893 portnr := omit
894 },
895 mgw := {
896 hostname := host_b,
897 portnr := omit
898 },
899 pt := pt,
900 codec := codec
901 }
Harald Weltef53f1642017-11-18 19:57:11 +0100902
Harald Weltebb7523b2018-03-29 08:52:01 +0200903 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
904 testcase TC_rtpem_selftest() runs on dummy_CT {
905 var RtpemStats stats[2];
906 var integer local_port := 10000;
907 var integer local_port2 := 20000;
908
909 f_init();
910
911 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
912 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
913
914 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
915 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
916
917 log("=== starting");
918 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
919 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
920
921 f_sleep(5.0);
922
923 log("=== stopping");
924 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
925 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
926 f_sleep(0.5);
927 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
928 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
929
930 stats[0] := f_rtpem_stats_get(RTPEM[0]);
931 stats[1] := f_rtpem_stats_get(RTPEM[1]);
932 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
933 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +0200934 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +0200935 }
936 setverdict(pass);
937 }
938
Philipp Maier2321ef92018-06-27 17:52:04 +0200939 /* Create one half open connection in receive-only mode. The MGW must accept
940 * the packets but must not send any. */
941 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
942 var RtpFlowData flow;
943 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
944 var MgcpCallId call_id := '1225'H;
945 var RtpemStats stats;
946
947 f_init(ep);
948 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
949 flow.em.portnr := 10000;
950 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, false);
951
952 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
953 f_sleep(1.0);
954 f_flow_delete(RTPEM[0], ep, call_id);
955
956 stats := f_rtpem_stats_get(RTPEM[0]);
957
958 if (stats.num_pkts_tx < 40) {
959 setverdict(fail);
960 }
961 if (stats.bytes_payload_tx < 190) {
962 setverdict(fail);
963 }
964 if (stats.num_pkts_rx != 0) {
965 setverdict(fail);
966 }
967 if (stats.num_pkts_rx_err_seq != 0) {
968 setverdict(fail);
969 }
970 if (stats.num_pkts_rx_err_ts != 0) {
971 setverdict(fail);
972 }
973 if (stats.num_pkts_rx_err_disabled != 0) {
974 setverdict(fail);
975 }
976
977 setverdict(pass);
978 }
979
980 /* Create one connection in loopback mode, test if the RTP packets are
981 * actually reflected */
982 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
983 var RtpFlowData flow;
984 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
985 var MgcpCallId call_id := '1225'H;
986 var RtpemStats stats;
987
988 f_init(ep);
989 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
990 flow.em.portnr := 10000;
991 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
992
993 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
994 f_sleep(1.0);
995 f_flow_delete(RTPEM[0], ep, call_id);
996
997 stats := f_rtpem_stats_get(RTPEM[0]);
998
999 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1000 setverdict(fail);
1001 }
1002 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1003 setverdict(fail);
1004 }
1005 if (stats.num_pkts_rx_err_seq != 0) {
1006 setverdict(fail);
1007 }
1008 if (stats.num_pkts_rx_err_ts != 0) {
1009 setverdict(fail);
1010 }
1011 if (stats.num_pkts_rx_err_disabled != 0) {
1012 setverdict(fail);
1013 }
1014
1015 setverdict(pass);
1016 }
1017
1018 function f_TC_two_crcx_and_rtp(boolean bidir) 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 */
1029 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1030 /* 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 */
1035 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1036 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
1065 setverdict(pass);
1066 }
1067
1068 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1069 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
1070 f_TC_two_crcx_and_rtp(false);
1071 }
1072
1073 /* create two local RTP emulations; create two connections on MGW EP,
1074 * exchange some data in both directions */
1075 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
1076 f_TC_two_crcx_and_rtp(true);
1077 }
1078
1079 /* create two local RTP emulations and pass data in both directions */
1080 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1081 var RtpFlowData flow[2];
1082 var RtpemStats stats[2];
1083 var MgcpResponse resp;
1084 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1085 var MgcpCallId call_id := '1227'H;
1086 var integer num_pkts_tx[2];
1087 var integer temp;
1088
1089 f_init(ep);
1090
1091 /* Create the first connection in receive only mode */
1092 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1093 flow[0].em.portnr := 10000;
1094 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1095
1096 /* Create the second connection. This connection will be also
1097 * in receive only mode */
1098 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1099 flow[1].em.portnr := 20000;
1100 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], false);
1101
1102 /* The first leg starts transmitting */
1103 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1104 f_sleep(0.5);
1105 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1106 if (stats[0].num_pkts_rx_err_disabled != 0) {
1107 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001108 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001109 }
1110 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1111 if (stats[1].num_pkts_rx_err_disabled != 0) {
1112 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001113 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001114 }
1115
1116 /* The second leg starts transmitting a little later */
1117 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1118 f_sleep(1.0);
1119 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1120 if (stats[0].num_pkts_rx_err_disabled != 0) {
1121 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001122 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001123 }
1124 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1125 if (stats[1].num_pkts_rx_err_disabled != 0) {
1126 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001127 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001128 }
1129
1130 /* The first leg will now be switched into bidirectional
1131 * mode, but we do not expect any data comming back yet. */
1132 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1133 f_sleep(0.5);
1134 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1135 if (stats[1].num_pkts_rx_err_disabled != 0) {
1136 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001137 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001138 }
1139 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1140 if (stats[1].num_pkts_rx_err_disabled != 0) {
1141 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001142 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001143 }
1144
1145 /* When the second leg is switched into bidirectional mode
1146 * as well, then the MGW will connect the two together and
1147 * we should see RTP streams passing through from both ends. */
1148 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1149 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1150 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1151 num_pkts_tx[0] := stats[0].num_pkts_tx
1152 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1153 num_pkts_tx[1] := stats[1].num_pkts_tx
1154
1155 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1156 f_sleep(2.0);
1157
1158 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1159 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1160
1161 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1162 if (temp > 3 or temp < -3) {
1163 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001164 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001165 }
1166
1167 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1168 if (temp > 3 or temp < -3) {
1169 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001170 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001171 }
1172
1173 /* Tear down */
1174 f_flow_delete(RTPEM[0]);
1175 f_flow_delete(RTPEM[1], ep, call_id);
1176 setverdict(pass);
1177 }
1178
1179 /* Test what happens when two RTP streams from different sources target
1180 * a single connection. Is the unsolicited stream properly ignored? */
1181 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1182 var RtpFlowData flow[2];
1183 var RtpemStats stats[2];
1184 var MgcpResponse resp;
1185 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1186 var MgcpCallId call_id := '1234321326'H;
1187 var integer unsolicited_port := 10002;
1188
1189 f_init(ep);
1190
1191 /* from us to MGW */
1192 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1193 /* bind local RTP emulation sockets */
1194 flow[0].em.portnr := 10000;
1195 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1196
1197 /* from MGW back to us */
1198 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1199 flow[1].em.portnr := 20000;
1200 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001201
1202 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1203 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1204
Philipp Maier2321ef92018-06-27 17:52:04 +02001205 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001206
Philipp Maier2321ef92018-06-27 17:52:04 +02001207 /* Start inserting unsolicited RTP packets */
Philipp Maier4de3e372018-06-29 17:15:22 +02001208 f_rtpem_bind(RTPEM[2], mp_local_ip, unsolicited_port);
1209 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001210 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1211
1212 f_sleep(0.5);
1213
1214 f_flow_delete(RTPEM[0]);
1215 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001216
1217 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1218 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1219 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1220 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001221 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001222 }
1223
Harald Weltebb7523b2018-03-29 08:52:01 +02001224 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001225 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001226
Philipp Maier2321ef92018-06-27 17:52:04 +02001227 /* Test a handover situation. We first create two connections transmit
1228 * some data bidirectionally. Then we will simulate a handover situation. */
1229 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1230 var RtpFlowData flow[2];
1231 var RtpemStats stats[3];
1232 var MgcpResponse resp;
1233 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1234 var MgcpCallId call_id := '76338'H;
1235 var integer port_old;
1236
1237 f_init(ep);
1238
1239 /* First connection (BTS) */
1240 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1241 /* bind local RTP emulation sockets */
1242 flow[0].em.portnr := 10000;
1243 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1244
1245 /* Second connection (PBX) */
1246 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1247 flow[1].em.portnr := 20000;
1248 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1249
1250 /* Normal rtp flow for one second */
1251 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1252 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1253 f_sleep(1.0);
1254
1255 /* Now switch the flow over to a new port (BTS) */
1256 port_old := flow[0].em.portnr;
1257 flow[0].em.portnr := 10002;
1258 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1259
1260 /* When handing over a call, the old source may still keep
1261 * transmitting for a while. We simulate this by injecting
1262 * some unsolicited packets on the behalf of the old source,
1263 * (old remote port) */
Philipp Maier4de3e372018-06-29 17:15:22 +02001264 f_rtpem_bind(RTPEM[2], mp_local_ip, port_old);
1265 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001266 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1267 f_sleep(1.0);
1268 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
1269 f_sleep(1.0);
1270
1271 /* Terminate call */
1272 f_flow_delete(RTPEM[0]);
1273 f_flow_delete(RTPEM[1], ep, call_id);
1274
1275 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1276 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1277 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
1278 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001279 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001280 }
1281 stats[2] := f_rtpem_stats_get(RTPEM[2]);
1282 if (stats[2].num_pkts_rx_err_disabled != 0) {
1283 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02001284 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001285 }
1286
1287 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02001288 }
Harald Weltef53f1642017-11-18 19:57:11 +01001289
Harald Weltee636afd2017-09-17 16:24:09 +08001290 /* TODO: Double-DLCX (no retransmission) */
1291
1292
1293
1294 /* TODO: AUEP (various) */
1295 /* TODO: RSIP (various) */
1296 /* TODO: RQNT (various) */
1297 /* TODO: EPCF (various) */
1298 /* TODO: AUCX (various) */
1299 /* TODO: invalid verb (various) */
1300
Harald Welte00a067f2017-09-13 23:27:17 +02001301 control {
1302 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08001303 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02001304 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001305 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08001306 execute(TC_crcx_unsupp_mode());
1307 execute(TC_crcx_early_bidir_mode());
1308 execute(TC_crcx_unsupp_param());
1309 execute(TC_crcx_missing_callid());
1310 execute(TC_crcx_missing_mode());
1311 execute(TC_crcx_unsupp_packet_intv());
1312 execute(TC_crcx_illegal_double_lco());
1313 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01001314 execute(TC_crcx_wildcarded());
1315 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08001316 execute(TC_mdcx_without_crcx());
1317 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01001318 execute(TC_mdcx_wildcarded());
1319 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01001320 execute(TC_crcx_and_dlcx_ep_callid_connid());
1321 execute(TC_crcx_and_dlcx_ep_callid());
1322 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01001323 execute(TC_crcx_and_dlcx_ep_callid_inval());
1324 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01001325 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01001326
1327 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02001328
1329 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02001330
1331 execute(TC_one_crcx_receive_only_rtp());
1332 execute(TC_one_crcx_loopback_rtp());
Harald Weltebb7523b2018-03-29 08:52:01 +02001333 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02001334 execute(TC_two_crcx_and_rtp_bidir());
1335 execute(TC_two_crcx_mdcx_and_rtp());
1336 execute(TC_two_crcx_and_unsolicited_rtp());
1337 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Harald Welte00a067f2017-09-13 23:27:17 +02001338 }
1339}