blob: ec3070e26b68369fd9a532af9d240de61479a42f [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;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020011 import from OSMUX_Types all;
12 import from OSMUX_CodecPort all;
13 import from OSMUX_CodecPort_CtrlFunct all;
14 import from OSMUX_Emulation all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080015 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010016 import from General_Types all;
17 import from Native_Functions all;
18 import from IPCP_Types all;
19 import from IP_Types all;
20 import from Osmocom_VTY_Functions all;
21 import from TELNETasp_PortType all;
Harald Welte00a067f2017-09-13 23:27:17 +020022
Philipp Maierbb7a01c2018-02-01 12:32:57 +010023 const charstring c_mgw_domain := "mgw";
24 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
25
Harald Welte21ba5572017-09-19 17:55:05 +080026 /* any variables declared in the component will be available to
27 * all functions that 'run on' the named component, similar to
28 * class members in C++ */
Harald Welte00a067f2017-09-13 23:27:17 +020029 type component dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080030 port MGCP_CODEC_PT MGCP;
31 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010032 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080033 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010034
Philipp Maier2321ef92018-06-27 17:52:04 +020035 var RTP_Emulation_CT vc_RTPEM[3];
36 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010037
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020038 var OSMUX_Emulation_CT vc_OsmuxEM;
39 port OsmuxEM_CTRL_PT OsmuxEM;
40
Philipp Maier6137c322019-02-20 16:13:41 +010041 port TELNETasp_PT MGWVTY;
Harald Welte00a067f2017-09-13 23:27:17 +020042 };
43
Harald Weltee1e18c52017-09-17 16:23:07 +080044 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
45 var MgcpTransId tid := int2str(g_trans_id);
46 g_trans_id := g_trans_id + 1;
47 return tid;
48 }
49
Harald Welte21ba5572017-09-19 17:55:05 +080050 /* all parameters declared here can be modified / overridden by
51 * the config file in the [MODULE_PARAMETERS] section. If no
52 * config file is used or the file doesn't specify them, the
53 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080054 modulepar {
55 PortNumber mp_local_udp_port := 2727;
56 charstring mp_local_ip := "127.0.0.1";
57 PortNumber mp_remote_udp_port := 2427;
58 charstring mp_remote_ip := "127.0.0.1";
Harald Weltef07c2862017-11-18 17:16:24 +010059 PortNumber mp_local_rtp_port_base := 10000;
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020060 PortNumber mp_local_osmux_port := 1985;
Harald Welte3c6ebb92017-09-16 00:56:57 +080061 }
62
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020063 private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT {
64 /* Turn on conversion mode */
65 f_vty_enter_config(MGWVTY);
66 f_vty_transceive(MGWVTY, "mgcp");
67 if (osmux_on) {
68 f_vty_transceive(MGWVTY, "osmux on");
69 } else {
70 f_vty_transceive(MGWVTY, "osmux off");
71 }
72 f_vty_transceive(MGWVTY, "exit");
73 f_vty_transceive(MGWVTY, "exit");
74
75 }
76
77 private function f_init_vty(boolean osmux_on) runs on dummy_CT {
Philipp Maier6137c322019-02-20 16:13:41 +010078 map(self:MGWVTY, system:MGWVTY);
79 f_vty_set_prompts(MGWVTY);
80 f_vty_transceive(MGWVTY, "enable");
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020081
82 f_vty_enable_osmux(osmux_on);
Philipp Maier6137c322019-02-20 16:13:41 +010083 }
84
Harald Weltebb7523b2018-03-29 08:52:01 +020085 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
86 runs on dummy_CT {
87 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
88 map(comp_ref:RTP, system:RTP);
89 map(comp_ref:RTCP, system:RTCP);
90 comp_ref.start(RTP_Emulation.f_main());
91 }
92
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020093 private function f_osmuxem_init(inout OSMUX_Emulation_CT comp_ref)
94 runs on dummy_CT {
95 comp_ref := OSMUX_Emulation_CT.create("OsmuxEM");
96 map(comp_ref:OSMUX, system:OSMUX);
97 comp_ref.start(OSMUX_Emulation.f_main());
98 }
99
Harald Welte21ba5572017-09-19 17:55:05 +0800100 /* initialization function, called by each test case at the
101 * beginning, but 'initialized' variable ensures its body is
102 * only executed once */
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200103 private function f_init(template MgcpEndpoint ep := omit, boolean osmux_on := false) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +0800104 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +0100105 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +0200106
Harald Welteedc45c12017-11-18 19:15:05 +0100107 if (initialized == false) {
108 initialized := true;
109
110 /* some random number for the initial transaction id */
111 g_trans_id := float2int(rnd()*65535.0);
112 map(self:MGCP, system:MGCP_CODEC_PT);
113 /* connect the MGCP test port using the given
114 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
115 * */
116 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 +0200117 if (not ispresent(res.connId)) {
118 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +0200119 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200120 }
Harald Welteedc45c12017-11-18 19:15:05 +0100121 g_mgcp_conn_id := res.connId;
122
Harald Weltebb7523b2018-03-29 08:52:01 +0200123 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
124 f_rtpem_init(vc_RTPEM[i], i);
125 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
126 }
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200127 if (osmux_on) {
128 f_osmuxem_init(vc_OsmuxEM);
129 connect(vc_OsmuxEM:CTRL, self:OsmuxEM);
130 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800131 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800132
Harald Welteedc45c12017-11-18 19:15:05 +0100133 if (isvalue(ep)) {
134 /* do a DLCX on all connections of the EP */
135 f_dlcx_ignore(valueof(ep));
136 }
Philipp Maier6137c322019-02-20 16:13:41 +0100137
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200138 f_init_vty(osmux_on);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800139 }
140
Harald Welte00a067f2017-09-13 23:27:17 +0200141 testcase TC_selftest() runs on dummy_CT {
142 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 +0100143 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 +0200144 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
145 "I: 1\n" &
146 "\n" &
147 "v=0\r\n" &
148 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
149 "s=-\r\n" &
150 "c=IN IP4 0.0.0.0\r\n" &
151 "t=0 0\r\n" &
152 "m=audio 0 RTP/AVP 126\r\n" &
153 "a=rtpmap:126 AMR/8000\r\n" &
154 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100155 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 +0200156 "M: sendrecv\r" &
157 "C: 2\r\n" &
158 "I: 1\r\n" &
159 "L: p:20, a:AMR, nt:IN\r\n" &
160 "\n" &
161 "v=0\r\n" &
162 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800163 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200164 "c=IN IP4 0.0.0.0\r\n" &
165 "t=0 0\r\n" &
166 "m=audio 4441 RTP/AVP 99\r\n" &
167 "a=rtpmap:99 AMR/8000\r\n" &
168 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800169 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200170
171 log(c_auep);
172 log(dec_MgcpCommand(c_auep));
173
174 log(c_mdcx3);
175 log(dec_MgcpCommand(c_mdcx3));
176
177 log(c_mdcx3_ret);
178 log(dec_MgcpResponse(c_mdcx3_ret));
179
180 log(c_mdcx4);
181 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800182
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100183 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
184 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 +0800185
186 log(c_crcx510_ret);
187 log(dec_MgcpResponse(c_crcx510_ret));
188 log(dec_MgcpMessage(c_crcx510_ret));
Daniel Willmann94e4e802018-12-13 16:56:26 +0100189
190 /* We didn't encounter any DTE, so pass the test */
191 setverdict(pass);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800192 }
193
Harald Weltee636afd2017-09-17 16:24:09 +0800194 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100195 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800196 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
197 * - CRCX with remote session description and without
198 *
199 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100200 * x packetization != 20ms
201 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800202 * x unsupported mode (517)
203 * x bidirectional mode before RemoteConnDesc: 527
204 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100205 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800206 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
207 */
208
Harald Welte21ba5572017-09-19 17:55:05 +0800209 /* build a receive template for receiving a MGCP message. You
210 * pass the MGCP response template in, and it will generate an
211 * MGCP_RecvFrom template that can match the primitives arriving on the
212 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800213 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
214 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100215 connId := g_mgcp_conn_id,
Harald Weltee636afd2017-09-17 16:24:09 +0800216 remName := mp_remote_ip,
217 remPort := mp_remote_udp_port,
218 locName := mp_local_ip,
219 locPort := mp_local_udp_port,
220 msg := { response := resp }
221 }
222 return mrf;
223 }
224
225 /* Send a MGCP request + receive a (matching!) response */
226 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
227 var MgcpMessage msg := { command := valueof(cmd) };
228 resp.line.trans_id := cmd.line.trans_id;
229 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800230 var MGCP_RecvFrom mrf;
231 timer T := 5.0;
232
Harald Welte55015362017-11-18 16:02:42 +0100233 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800234 T.start;
235 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800236 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200237 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
238 setverdict(fail, "Response didn't match template");
239 mtc.stop;
240 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800241 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200242 [] T.timeout {
243 setverdict(fail, "Timeout waiting for response to ", cmd);
244 mtc.stop;
245 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800246 }
247 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800248
249 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
250 return mrf.msg.response;
251 } else {
252 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
253 return r;
254 }
Harald Welte00a067f2017-09-13 23:27:17 +0200255 }
256
Harald Welteba62c8c2017-11-18 18:26:49 +0100257 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
258 var integer i;
259 for (i := 0; i < lengthof(resp.params); i := i + 1) {
260 var MgcpParameter par := resp.params[i];
261 if (par.code == "I") {
262 return str2hex(par.val);
263 }
264 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200265 setverdict(fail, "Could not find conn id for MgcpReponse");
266 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100267 return '00000000'H;
268 }
269
Harald Welte10889c12017-11-18 19:40:31 +0100270 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
271 template MgcpCallId call_id := omit,
272 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100273 var template MgcpCommand cmd;
274 var MgcpResponse resp;
275 var template MgcpResponse rtmpl := {
276 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100277 code := ret_code,
278 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100279 },
280 params := *,
281 sdp := *
282 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100283 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100284 resp := mgcp_transceive_mgw(cmd, rtmpl);
285 }
286
Harald Welte10889c12017-11-18 19:40:31 +0100287 /* Send DLCX and expect OK response */
288 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
289 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100290 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100291 }
292
Harald Welteba62c8c2017-11-18 18:26:49 +0100293 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100294 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100295 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100296 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100297 }
298
Harald Weltebb7523b2018-03-29 08:52:01 +0200299 type record HostPort {
300 charstring hostname,
301 integer portnr optional
302 }
303 type record RtpFlowData {
304 HostPort em, /* emulation side */
305 HostPort mgw, /* mgw side */
306 uint7_t pt,
307 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200308 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100309 RtpemConfig rtp_cfg optional,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200310 boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */
311 MgcpOsmuxCID osmux_cid optional,
312 MgcpOsmuxCID osmux_cid_response optional,
313 OsmuxemConfig osmux_cfg optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100314 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200315 }
316
Philipp Maier2321ef92018-06-27 17:52:04 +0200317 /* Create an RTP flow (bidirectional, or receive-only) */
318 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 +0200319 boolean one_phase := true)
320 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200321 var template MgcpCommand cmd;
322 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100323 var SDP_attribute_list attributes;
324
325 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
326 if (isvalue(flow.fmtp)) {
327 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
328 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200329
330 /* bind local RTP emulation socket */
331 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
332
Philipp Maier28bb8292018-07-20 17:09:17 +0200333 /* configure rtp-emulation */
334 if (ispresent(flow.rtp_cfg)) {
335 f_rtpem_configure(pt, flow.rtp_cfg);
336 } else {
337 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
338 rtp_cfg.tx_payload_type := flow.pt
339 f_rtpem_configure(pt, rtp_cfg);
340 }
341
Harald Weltebb7523b2018-03-29 08:52:01 +0200342 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200343 /* Connect flow to MGW using a CRCX that also contains an SDP
344 * part that tells the MGW where we are listening for RTP streams
345 * that come from the MGW. We get a fully working connection in
346 * one go. */
347
348 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200349 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100350 flow.em.portnr, { int2str(flow.pt) }, attributes);
351
Harald Weltebb7523b2018-03-29 08:52:01 +0200352 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
353 flow.mgcp_conn_id := extract_conn_id(resp);
354 /* extract port number from response */
355 flow.mgw.portnr :=
356 resp.sdp.media_list[0].media_field.ports.port_number;
357 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200358 /* Create a half-open connection only. We do not tell the MGW
359 * where it can send RTP streams to us. This means this
360 * connection will only be able to receive but can not send
361 * data back to us. In order to turn the connection in a fully
362 * bi-directional one, a separate MDCX is needed. */
363
364 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200365 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
366 flow.mgcp_conn_id := extract_conn_id(resp);
367 /* extract MGW-side port number from response */
368 flow.mgw.portnr :=
369 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200370 }
371 /* finally, connect the emulation-side RTP socket to the MGW */
372 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
373 }
374
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200375 /* Create an Osmux flow (bidirectional, or receive-only) */
376 function f_flow_create_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
377 boolean one_phase := true)
378 runs on dummy_CT {
379 var template MgcpCommand cmd;
380 var MgcpResponse resp;
381 var SDP_attribute_list attributes;
382 var OsmuxTxHandle tx_hdl;
383 var OsmuxRxHandle rx_hdl;
384 var charstring cid_response;
385 var OsmuxCID cid_resp_parsed
386
387 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
388 if (isvalue(flow.fmtp)) {
389 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
390 }
391
392 /* bind local Osmux emulation socket */
393 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
394
395 /* configure osmux-emulation */
396 if (ispresent(flow.osmux_cfg)) {
397 f_osmuxem_configure(pt, flow.osmux_cfg);
398 } else {
399 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
400 f_osmuxem_configure(pt, osmux_cfg);
401 flow.osmux_cfg := osmux_cfg
402 }
403
404 if (one_phase) {
405 /* Connect flow to MGW using a CRCX that also contains an SDP
406 * part that tells the MGW where we are listening for Osmux streams
407 * that come from the MGW. We get a fully working connection in
408 * one go. */
409 rx_hdl := c_OsmuxemDefaultRxHandle;
410 rx_hdl.cid := flow.osmux_cid;
411 f_osmuxem_register_rxhandle(pt, rx_hdl);
412 flow.osmux_cid_sent := true;
413 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
414 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
415 flow.em.portnr, { int2str(flow.pt) }, attributes);
416 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
417 flow.mgcp_conn_id := extract_conn_id(resp);
418 /* extract port number from response */
419 flow.mgw.portnr :=
420 resp.sdp.media_list[0].media_field.ports.port_number;
421 } else {
422 /* Create a half-open connection only. We do not tell the MGW
423 * where it can send Osmux streams to us. This means this
424 * connection will only be able to receive but can not send
425 * data back to us. In order to turn the connection in a fully
426 * bi-directional one, a separate MDCX is needed. */
427
428 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid);
429 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
430
431 flow.mgcp_conn_id := extract_conn_id(resp);
432 /* extract MGW-side port number from response */
433 flow.mgw.portnr :=
434 resp.sdp.media_list[0].media_field.ports.port_number;
435 }
436
437 /* extract Osmux CID we got assigned by the MGW */
438 var MgcpMessage resp_msg := {
439 response := resp
440 }
441
442 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
443 setverdict(fail, "No Osmux CID in MGCP response", resp);
444 mtc.stop;
445 }
446
447 /* Make sure response is no wildcard */
448 flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response);
449 if (flow.osmux_cid_response == -1) {
450 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
451 mtc.stop;
452 }
453 tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response));
454 f_osmuxem_register_txhandle(pt, tx_hdl);
455
456 /* finally, connect the emulation-side RTP socket to the MGW */
457 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
458 }
459
Philipp Maier2321ef92018-06-27 17:52:04 +0200460 /* Modify an existing RTP flow */
461 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
462 runs on dummy_CT {
463 var template MgcpCommand cmd;
464 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100465 var SDP_attribute_list attributes;
466
467 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
468 if (isvalue(flow.fmtp)) {
469 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
470 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200471
472 /* rebind local RTP emulation socket to the new address */
473 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
474
Philipp Maier28bb8292018-07-20 17:09:17 +0200475 /* reconfigure rtp-emulation */
476 if (ispresent(flow.rtp_cfg)) {
477 f_rtpem_configure(pt, flow.rtp_cfg);
478 } else {
479 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
480 rtp_cfg.tx_payload_type := flow.pt
481 f_rtpem_configure(pt, rtp_cfg);
482 }
483
Philipp Maier2321ef92018-06-27 17:52:04 +0200484 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
485 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
486 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100487 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200488 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
489
490 /* extract MGW-side port number from response. (usually this
491 * will not change, but thats is up to the MGW) */
492 flow.mgw.portnr :=
493 resp.sdp.media_list[0].media_field.ports.port_number;
494
495 /* reconnect the emulation-side RTP socket to the MGW */
496 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
497 }
498
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200499 /* Modify an existing Osmux flow */
500 function f_flow_modify_osmux(OsmuxEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
501 runs on dummy_CT {
502 var template MgcpCommand cmd;
503 var MgcpResponse resp;
504 var SDP_attribute_list attributes;
505 var OsmuxRxHandle rx_hdl;
506 var charstring cid_response;
507 var OsmuxCID cid_resp_parsed
508
509 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
510 if (isvalue(flow.fmtp)) {
511 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
512 }
513
514 /* rebind local Osmux emulation socket to the new address */
515 f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
516
517 /* configure osmux-emulation */
518 if (ispresent(flow.osmux_cfg)) {
519 f_osmuxem_configure(pt, flow.osmux_cfg);
520 } else {
521 var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg;
522 f_osmuxem_configure(pt, osmux_cfg);
523 }
524
525 /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */
526 if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) {
527 rx_hdl := c_OsmuxemDefaultRxHandle;
528 rx_hdl.cid := flow.osmux_cid;
529 f_osmuxem_register_rxhandle(pt, rx_hdl);
530 flow.osmux_cid_sent := true;
531 }
532
533 /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
534 cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid);
535 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
536 flow.em.portnr, { int2str(flow.pt) }, attributes);
537 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
538
539 /* extract MGW-side port number from response. (usually this
540 * will not change, but thats is up to the MGW) */
541 flow.mgw.portnr :=
542 resp.sdp.media_list[0].media_field.ports.port_number;
543
544 /* extract Osmux CID we got assigned by the MGW */
545 var MgcpMessage resp_msg := {
546 response := resp
547 }
548
549 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
550 setverdict(fail, "No Osmux CID in MGCP response", resp);
551 mtc.stop;
552 }
553
554 /* Make sure response is no wildcard */
555 cid_resp_parsed := f_mgcp_osmux_cid_decode(cid_response);
556 if (cid_resp_parsed == -1) {
557 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
558 mtc.stop;
559 }
560 if (cid_resp_parsed != flow.osmux_cid_response) {
561 setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value");
562 mtc.stop;
563 }
564
565 /* reconnect the emulation-side Osmux socket to the MGW */
566 f_osmuxem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
567 }
568
Philipp Maier2321ef92018-06-27 17:52:04 +0200569 /* Delete an existing RTP flow */
570 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
571 runs on dummy_CT {
572 var template MgcpCommand cmd;
573 var MgcpResponse resp;
574
575 /* Switch off RTP flow */
576 f_rtpem_mode(pt, RTPEM_MODE_NONE);
577
578 /* Delete connection on MGW (if needed) */
579 if (isvalue(call_id) and isvalue(ep)) {
580 f_sleep(0.1);
581 f_dlcx_ok(valueof(ep), call_id);
582 }
583 }
584
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200585 /* Delete an existing Osmux flow */
586 function f_flow_delete_osmux(OsmuxEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
587 runs on dummy_CT {
588 var template MgcpCommand cmd;
589 var MgcpResponse resp;
590
591 /* Switch off Osmux flow */
592 f_osmuxem_mode(pt, OSMUXEM_MODE_NONE);
593
594 /* Delete connection on MGW (if needed) */
595 if (isvalue(call_id) and isvalue(ep)) {
596 f_sleep(0.1);
597 f_dlcx_ok(valueof(ep), call_id);
598 }
599 }
600
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100601 function f_crcx(charstring ep_prefix) runs on dummy_CT {
602 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800603 var template MgcpCommand cmd;
604 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100605 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800606
Harald Welteedc45c12017-11-18 19:15:05 +0100607 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800608
Harald Welteba62c8c2017-11-18 18:26:49 +0100609 /* create the connection on the MGW */
610 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100611 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100612 extract_conn_id(resp);
613
614 /* clean-up */
615 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100616 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100617
Philipp Maier45635f42018-06-05 17:28:02 +0200618 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
619 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
620 var template MgcpCommand cmd;
621 var MgcpResponse resp;
622 var MgcpCallId call_id := '1234'H;
623
624 f_init(ep);
625
626 /* create the connection on the MGW */
627 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
628 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
629 extract_conn_id(resp);
630
631 /* clean-up */
632 f_dlcx_ok(ep, call_id);
633
634 /* See also OS#2658: Even when we omit the LCO information, we
635 expect the MGW to pick a sane payload type for us. This
636 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200637 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200638 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200639 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200640 }
641
642 /* See also OS#2658: We also expect the MGW to assign a port
643 number to us. */
644 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
645 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200646 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200647 }
648 }
649
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200650 function f_crcx_osmux(charstring ep_prefix, MgcpOsmuxCID osmux_cid, boolean run_init := true) runs on dummy_CT {
651 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
652 var template MgcpCommand cmd;
653 var MgcpResponse resp;
654 var MgcpCallId call_id := '1234'H;
655 var charstring cid_response;
656
657 if (run_init) {
658 f_init(ep, true);
659 }
660
661 /* create the connection on the MGW */
662 cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", call_id, osmux_cid);
663 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
664 extract_conn_id(resp);
665
666 /* extract Osmux CID we got assigned by the MGW */
667 var MgcpMessage resp_msg := {
668 response := resp
669 }
670
671 if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) {
672 setverdict(fail, "No Osmux CID in MGCP response", resp);
673 mtc.stop;
674 }
675
676 /* Make sure response is no wildcard */
677 if (f_mgcp_osmux_cid_decode(cid_response) == -1) {
678 setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard");
679 mtc.stop;
680 }
681
682 /* clean-up */
683 f_dlcx_ok(ep, call_id);
684 }
685
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100686 /* test valid CRCX without SDP */
687 testcase TC_crcx() runs on dummy_CT {
688 f_crcx(c_mgw_ep_rtpbridge);
689 setverdict(pass);
690 }
691
Philipp Maier45635f42018-06-05 17:28:02 +0200692 /* test valid CRCX without SDP and LCO */
693 testcase TC_crcx_no_lco() runs on dummy_CT {
694 f_crcx_no_lco(c_mgw_ep_rtpbridge);
695 setverdict(pass);
696 }
697
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100698 /* test valid CRCX without SDP (older method without endpoint prefix) */
699 testcase TC_crcx_noprefix() runs on dummy_CT {
700 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800701 setverdict(pass);
702 }
703
704 /* test CRCX with unsupported mode, expect 517 */
705 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
706 var template MgcpCommand cmd;
707 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100708 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100709 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800710 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
711
Harald Welteedc45c12017-11-18 19:15:05 +0100712 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800713
Harald Welteba62c8c2017-11-18 18:26:49 +0100714 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800715 resp := mgcp_transceive_mgw(cmd, rtmpl);
716 setverdict(pass);
717 }
718
Harald Welte21ba5572017-09-19 17:55:05 +0800719 /* test CRCX with early bi-directional mode, expect 527 as
720 * bi-diretional media can only be established once both local and
721 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800722 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
723 var template MgcpCommand cmd;
724 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100725 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100726 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800727 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
728
Harald Welteedc45c12017-11-18 19:15:05 +0100729 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800730
Harald Welteba62c8c2017-11-18 18:26:49 +0100731 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800732 resp := mgcp_transceive_mgw(cmd, rtmpl);
733 setverdict(pass);
734 }
735
736 /* test CRCX with unsupported Parameters */
737 testcase TC_crcx_unsupp_param() runs on dummy_CT {
738 var template MgcpCommand cmd;
739 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100740 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100741 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800742 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
743
Harald Welteedc45c12017-11-18 19:15:05 +0100744 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800745
Harald Welteba62c8c2017-11-18 18:26:49 +0100746 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100747 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
748 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
749
Harald Weltee636afd2017-09-17 16:24:09 +0800750 resp := mgcp_transceive_mgw(cmd, rtmpl);
751 setverdict(pass);
752 }
753
754 /* test CRCX with missing CallId */
755 testcase TC_crcx_missing_callid() runs on dummy_CT {
756 var template MgcpCommand cmd;
757 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100758 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100759 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800760
Harald Welteedc45c12017-11-18 19:15:05 +0100761 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800762
Harald Welteba62c8c2017-11-18 18:26:49 +0100763 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800764 cmd.params := {
765 t_MgcpParConnMode("recvonly"),
766 t_MgcpParLocConnOpt("p:20")
767 }
768 resp := mgcp_transceive_mgw(cmd, rtmpl);
769 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100770
Harald Weltee636afd2017-09-17 16:24:09 +0800771 }
772
773 /* test CRCX with missing Mode */
774 testcase TC_crcx_missing_mode() runs on dummy_CT {
775 var template MgcpCommand cmd;
776 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100777 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100778 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100779 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800780
Harald Welteedc45c12017-11-18 19:15:05 +0100781 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800782
Harald Welteba62c8c2017-11-18 18:26:49 +0100783 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800784 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100785 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800786 t_MgcpParLocConnOpt("p:20")
787 }
788 resp := mgcp_transceive_mgw(cmd, rtmpl);
789 setverdict(pass);
790 }
791
792 /* test CRCX with unsupported packetization interval */
793 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
794 var template MgcpCommand cmd;
795 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100796 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100797 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100798 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800799
Harald Welteedc45c12017-11-18 19:15:05 +0100800 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800801
Harald Welteba62c8c2017-11-18 18:26:49 +0100802 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100803 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800804 resp := mgcp_transceive_mgw(cmd, rtmpl);
805 setverdict(pass);
806 }
807
808 /* test CRCX with illegal double presence of local connection option */
809 testcase TC_crcx_illegal_double_lco() 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 & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100813 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800814 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
815
Harald Welteedc45c12017-11-18 19:15:05 +0100816 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800817
Harald Welteba62c8c2017-11-18 18:26:49 +0100818 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100819 /* p:20 is permitted only once and not twice! */
820 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800821 resp := mgcp_transceive_mgw(cmd, rtmpl);
822 setverdict(pass);
823 }
824
825 /* test valid CRCX with valid SDP */
826 testcase TC_crcx_sdp() runs on dummy_CT {
827 var template MgcpCommand cmd;
828 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100829 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100830 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800831
Harald Welteedc45c12017-11-18 19:15:05 +0100832 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800833
Harald Welteba62c8c2017-11-18 18:26:49 +0100834 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800835 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
836 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
837 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100838 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100839
840 /* clean-up */
841 f_dlcx_ok(ep, call_id);
842
Harald Weltee636afd2017-09-17 16:24:09 +0800843 setverdict(pass);
844 }
845
Philipp Maier5e06cee2018-02-01 18:28:08 +0100846 /* test valid wildcarded CRCX */
847 testcase TC_crcx_wildcarded() runs on dummy_CT {
848 var template MgcpCommand cmd;
849 var MgcpResponse resp;
850 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
851 var MgcpCallId call_id := '1234'H;
852 var MgcpEndpoint ep_assigned;
853 f_init();
854
855 /* create the connection on the MGW */
856 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
857 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
858 extract_conn_id(resp);
859
860 /* extract endpoint name we got assigned by the MGW */
861 var MgcpMessage resp_msg := {
862 response := resp
863 }
864 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
865 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200866 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100867 }
868
869 /* clean-up */
870 f_dlcx_ok(ep_assigned, call_id);
871
872 setverdict(pass);
873 }
874
875 /* test valid wildcarded CRCX */
876 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
877 const integer n_endpoints := 32;
878 var integer i;
879 var template MgcpCommand cmd;
880 var MgcpResponse resp;
881 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
882 var MgcpCallId call_id := '1234'H;
883 var MgcpEndpoint ep_assigned[n_endpoints];
884 f_init();
885
886 /* Exhaust all endpoint resources on the virtual trunk */
887 for (i := 0; i < n_endpoints; i := i+1) {
888 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
889 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
890
891 /* Make sure we got a connection id */
892 extract_conn_id(resp);
893
894 var MgcpMessage resp_msg := {
895 response := resp
896 }
897 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
898 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200899 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100900 }
901 }
902
903 /* Try to allocate one more endpoint, which should fail */
904 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
905 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
906 resp := mgcp_transceive_mgw(cmd, rtmpl);
907 setverdict(pass);
908
909 /* clean-up */
910 for (i := 0; i < n_endpoints; i := i+1) {
911 f_dlcx_ok(ep_assigned[i], call_id);
912 }
913 setverdict(pass);
914 }
915
Harald Weltee636afd2017-09-17 16:24:09 +0800916 /* TODO: various SDP related bits */
917
918
919 /* TODO: CRCX with X-Osmux */
920 /* TODO: double CRCX without force_realloc */
921
922 /* TODO: MDCX (various) */
923
924 /* TODO: MDCX without CRCX first */
925 testcase TC_mdcx_without_crcx() runs on dummy_CT {
926 var template MgcpCommand cmd;
927 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100928 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100929 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800930 var template MgcpResponse rtmpl := {
931 line := {
932 /* TODO: accept/enforce better error? */
933 code := "400",
934 string := ?
935 },
936 params:= { },
937 sdp := omit
938 };
939
Harald Welteedc45c12017-11-18 19:15:05 +0100940 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800941
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100942 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800943 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
944 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
945 valueof(ts_SDP_ptime(20)) });
946 resp := mgcp_transceive_mgw(cmd, rtmpl);
947 setverdict(pass);
948 }
949
950 /* DLCX without CRCX first */
951 testcase TC_dlcx_without_crcx() runs on dummy_CT {
952 var template MgcpCommand cmd;
953 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100954 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800955 var template MgcpResponse rtmpl := {
956 line := {
Harald Weltef91edf32017-12-28 14:16:21 +0100957 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +0800958 string := ?
959 },
960 params:= { },
961 sdp := omit
962 };
963
Harald Welteedc45c12017-11-18 19:15:05 +0100964 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800965
Harald Welteedc45c12017-11-18 19:15:05 +0100966 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800967 resp := mgcp_transceive_mgw(cmd, rtmpl);
968 setverdict(pass);
969 }
970
Philipp Maier8a3dc922018-02-02 14:55:12 +0100971 /* test valid wildcarded MDCX */
972 testcase TC_mdcx_wildcarded() runs on dummy_CT {
973 /* Note: A wildcarded MDCX is not allowed, so we expect the
974 * MGW to reject this request */
975 var template MgcpCommand cmd;
976 var MgcpResponse resp;
977 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
978 var MgcpCallId call_id := '1225'H;
979 var template MgcpResponse rtmpl := {
980 line := {
981 /* TODO: accept/enforce better error? */
982 code := "507",
983 string := ?
984 },
985 params:= { },
986 sdp := omit
987 };
988
989 f_init(ep);
990
991 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
992 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
993 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
994 valueof(ts_SDP_ptime(20)) });
995 resp := mgcp_transceive_mgw(cmd, rtmpl);
996 setverdict(pass);
997 }
998
999 /* test valid wildcarded DLCX */
1000 testcase TC_dlcx_wildcarded() runs on dummy_CT {
1001 /* Note: A wildcarded DLCX is specified, but our MGW does not
1002 * support this feature so we expect the MGW to reject the
1003 * request */
1004 var template MgcpCommand cmd;
1005 var MgcpResponse resp;
1006 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
1007 var template MgcpResponse rtmpl := {
1008 line := {
1009 code := "507",
1010 string := ?
1011 },
1012 params:= { },
1013 sdp := omit
1014 };
1015
1016 f_init(ep);
1017
1018 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
1019 resp := mgcp_transceive_mgw(cmd, rtmpl);
1020 setverdict(pass);
1021 }
1022
Harald Welte79181ff2017-11-18 19:26:11 +01001023 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
1024 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
1025 var template MgcpCommand cmd;
1026 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001027 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +01001028 var MgcpCallId call_id := '51234'H;
1029
1030 f_init(ep);
1031
1032 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1033 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1034
1035 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1036
1037 setverdict(pass);
1038 }
1039
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001040 /* test valid CRCX without SDP */
1041 testcase TC_crcx_osmux_wildcard() runs on dummy_CT {
1042 f_crcx_osmux(c_mgw_ep_rtpbridge, -1);
1043 setverdict(pass);
1044 }
1045
1046 /* test valid CRCX without SDP */
1047 testcase TC_crcx_osmux_fixed() runs on dummy_CT {
1048 f_crcx_osmux(c_mgw_ep_rtpbridge, 2);
1049 setverdict(pass);
1050 }
1051
1052 /* test valid CRCX without SDP, twice, to make sure CID is freed fine during first step. */
1053 testcase TC_crcx_osmux_fixed_twice() runs on dummy_CT {
1054 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, true);
1055 f_crcx_osmux(c_mgw_ep_rtpbridge, 3, false);
1056 setverdict(pass);
1057 }
1058
1059 /* Create one half open connection in receive-only mode. The MGW must accept
1060 * the packets but must not send any. */
1061 testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT {
1062 var RtpFlowData flow;
1063 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1064 var MgcpCallId call_id := '1225'H;
1065 var OsmuxemStats stats;
1066 var OsmuxTxHandle tx_hdl;
1067
1068 f_init(ep, true);
1069 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
1070 flow.em.portnr := mp_local_osmux_port;
1071 flow.osmux_cid := -1;
1072 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false);
1073
1074 /* create a transmitter not yet known by MGW */
1075 tx_hdl := valueof(t_TxHandleAMR590(2));
1076 f_osmuxem_register_txhandle(OsmuxEM, tx_hdl);
1077
1078 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1079 f_sleep(1.0);
1080 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1081
1082 stats := f_osmuxem_stats_get(OsmuxEM);
1083
1084 if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) {
1085 setverdict(fail);
1086 }
1087 if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) {
1088 setverdict(fail);
1089 }
1090
1091 f_osmuxem_stats_err_check(stats);
1092
1093 setverdict(pass);
1094 }
1095
1096 /* Create one connection in loopback mode, test if the Osmux packets are
1097 * actually reflected */
1098 testcase TC_one_crcx_loopback_osmux() runs on dummy_CT {
1099 var RtpFlowData flow;
1100 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1101 var MgcpCallId call_id := '1225'H;
1102 var OsmuxemStats stats;
1103 var OsmuxTxHandle tx_hdl;
1104
1105 f_init(ep, true);
1106 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1107 flow.em.portnr := mp_local_osmux_port;
1108 flow.osmux_cid := 2;
1109 f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow);
1110
1111 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1112 f_sleep(1.0);
1113
1114 /* Switch off both Tx, wait to receive delayed frames from MGW */
1115 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1116 f_sleep(0.1);
1117 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1118
1119 stats := f_osmuxem_stats_get(OsmuxEM);
1120
1121 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1122 setverdict(fail);
1123 }
1124 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1125 setverdict(fail);
1126 }
1127
1128 f_osmuxem_stats_err_check(stats);
1129
1130 setverdict(pass);
1131 }
1132
1133 /* Cross-compare two osmuxem-statistics. The transmission statistics on the a side
1134 * must match the reception statistics on the other side and vice versa. The
1135 * user may also supply a tolerance value (number of packets) when deviations
1136 * are acceptable */
1137 function f_rtp_osmux_stats_compare(RtpemStats a, OsmuxemStats b, integer batch_size, integer tolerance := 0) return boolean {
1138 var integer plen;
1139
1140 log("stats A: ", a);
1141 log("stats B: ", b);
1142 log("tolerance: ", tolerance, " packets");
1143 log("batch_size: ", batch_size, " packets");
1144
1145 var integer tolerance_batch := tolerance + (batch_size - tolerance mod batch_size);
1146
1147 if (f_osmuxem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx * batch_size, tolerance_batch) == false) {
1148 return false;
1149 }
1150
1151 if (f_osmuxem_stats_compare_value(a.num_pkts_rx / batch_size, b.num_pkts_tx, tolerance_batch) == false) {
1152 return false;
1153 }
1154
1155 if(a.num_pkts_tx > 0) {
1156 plen := a.bytes_payload_tx / a.num_pkts_tx;
1157 } else {
1158 plen := 0;
1159 }
1160
1161 /* Each RTP pcket payload contains 2 extra bytes due to AMR ToC at start */
1162 if (f_osmuxem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx + a.num_pkts_tx * 2, tolerance_batch * plen) == false) {
1163 log("incorrect payload A->B: " , a.bytes_payload_tx, " vs ", b.bytes_payload_rx + a.num_pkts_rx * 2);
1164 return false;
1165 }
1166
1167 if (f_osmuxem_stats_compare_value(a.bytes_payload_rx, b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, tolerance_batch * plen) == false) {
1168 log("incorrect payload B->A: " , b.bytes_payload_tx + b.num_pkts_tx * 2 * batch_size, " vs ", a.bytes_payload_rx);
1169 return false;
1170 }
1171
1172 return true;
1173 }
1174
1175 function f_TC_two_crcx_and_rtp_osmux(boolean bidir) runs on dummy_CT {
1176 var RtpFlowData flow[2];
1177 var RtpemStats stats_rtp;
1178 var OsmuxemStats stats_osmux;
1179 var MgcpResponse resp;
1180 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1181 var MgcpCallId call_id := '1226'H;
1182 var integer tolerance := 0;
1183
1184 f_init(ep, true);
1185
1186 /* from us to MGW */
1187 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1188 flow[0].rtp_cfg := c_RtpemDefaultCfg
1189 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1190 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1191 flow[0].rtp_cfg.rx_fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload);
1192 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
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, 110, "AMR/8000"));
1199 flow[1].em.portnr := mp_local_osmux_port;
1200 flow[1].osmux_cid := 2;
1201 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1202 f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1203
1204 if (bidir) {
1205 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1206 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1207
1208 /* Note: When we test bidirectional we may
1209 * loose packets during switch off because
1210 * both ends are transmitting and we only
1211 * can switch them off one by one. */
1212 tolerance := 3;
1213 } else {
1214 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1215 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1216 }
1217
1218 f_sleep(1.0);
1219
1220 /* Switch off both Tx, wait to receive delayed frames from MGW */
1221 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1222 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_RXONLY);
1223 f_sleep(0.1);
1224
1225 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1226 f_flow_delete(RTPEM[1]);
1227
1228 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1229 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1230 if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) {
1231 setverdict(fail, "RTP and Osmux endpoint statistics don't match");
1232 mtc.stop;
1233 }
1234
1235 f_rtpem_stats_err_check(stats_rtp);
1236 f_osmuxem_stats_err_check(stats_osmux);
1237
1238 setverdict(pass);
1239 }
1240
1241 /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */
1242 testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT {
1243 f_TC_two_crcx_and_rtp_osmux(false);
1244 }
1245
1246 /* create one RTP and one OSmux emulations; create two connections on MGW EP,
1247 * exchange some data in both directions */
1248 testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT {
1249 f_TC_two_crcx_and_rtp_osmux(true);
1250 }
1251
1252
1253 function f_two_crcx_mdcx_and_rtp_osmux(boolean crcx_osmux_wildcard) runs on dummy_CT {
1254 var RtpFlowData flow[2];
1255 var RtpemStats stats_rtp;
1256 var OsmuxemStats stats_osmux;
1257 var MgcpResponse resp;
1258 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1259 var MgcpCallId call_id := '1227'H;
1260 var integer num_pkts_tx[2];
1261 var integer temp;
1262
1263 f_init(ep, true);
1264
1265 /* Create the first connection in receive only mode */
1266 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1267 flow[0].rtp_cfg := c_RtpemDefaultCfg
1268 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1269 /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
1270 flow[0].rtp_cfg.rx_fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload);
1271 flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
1272 /* bind local RTP emulation sockets */
1273 flow[0].em.portnr := 10000;
1274 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1275
1276
1277 /* Create the second connection. This connection will be also
1278 * in receive only mode */
1279 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "AMR/8000"));
1280 flow[1].em.portnr := mp_local_osmux_port;
1281 if (crcx_osmux_wildcard) {
1282 flow[1].osmux_cid := -1;
1283 } else {
1284 flow[1].osmux_cid := 2;
1285 }
1286 flow[1].osmux_cfg := c_OsmuxemDefaultCfg;
1287 f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], false);
1288
1289
1290 /* The first leg starts transmitting */
1291 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1292 f_sleep(0.5);
1293 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1294 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1295 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1296 mtc.stop;
1297 }
1298 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1299 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1300 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1301 mtc.stop;
1302 }
1303
1304 /* The second leg starts transmitting a little later */
1305 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_TXONLY);
1306 f_sleep(1.0);
1307 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1308 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1309 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1310 mtc.stop;
1311 }
1312 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1313 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1314 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1315 mtc.stop;
1316 }
1317
1318 /* The first leg will now be switched into bidirectional
1319 * mode, but we do not expect any data comming back yet. */
1320 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1321 f_sleep(0.5);
1322 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1323 if (stats_rtp.num_pkts_rx_err_disabled != 0) {
1324 setverdict(fail, "received packets from RTP MGW on recvonly connection");
1325 mtc.stop;
1326 }
1327 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1328 if (stats_osmux.num_pkts_rx_err_disabled != 0) {
1329 setverdict(fail, "received packets from Osmux MGW on recvonly connection");
1330 mtc.stop;
1331 }
1332
1333 /* When the second leg is switched into bidirectional mode
1334 * as well, then the MGW will connect the two together and
1335 * we should see RTP streams passing through from both ends. */
1336 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1337 f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR);
1338 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1339 num_pkts_tx[0] := stats_rtp.num_pkts_tx
1340 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1341 num_pkts_tx[1] := stats_osmux.num_pkts_tx
1342
1343 if (crcx_osmux_wildcard) {
1344 /* For now we must set same CID as the MGW recvCID,
1345 * having sendCID!=recvCID is not yet supported. */
1346 flow[1].osmux_cid := flow[1].osmux_cid_response;
1347 }
1348 f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]);
1349 f_sleep(2.0);
1350
1351 stats_rtp := f_rtpem_stats_get(RTPEM[0]);
1352 stats_osmux := f_osmuxem_stats_get(OsmuxEM);
1353
1354 temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size;
1355 if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) {
1356 log("stats_rtp: ", stats_rtp);
1357 log("stats_osmux: ", stats_osmux);
1358 log("old_rtp_tx: ", num_pkts_tx[0]);
1359 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1360 mtc.stop;
1361 }
1362
1363 temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size;
1364 if (temp > 3 or temp < -3) {
1365 setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")");
1366 mtc.stop;
1367 }
1368
1369 f_rtpem_stats_err_check(stats_rtp);
1370 f_osmuxem_stats_err_check(stats_osmux);
1371
1372 /* Tear down */
1373 f_flow_delete(RTPEM[0]);
1374 f_flow_delete_osmux(OsmuxEM, ep, call_id);
1375 setverdict(pass);
1376 }
1377
1378 /* create one RTP and one OSmux emulations and pass data in both
1379 directions. Create CRCX with wildcard Osmux CID and set it later
1380 during MDCX. This is similar to how MSC sets up the call in AoIP. */
1381 testcase TC_two_crcx_mdcx_and_rtp_osmux_wildcard() runs on dummy_CT {
1382 f_two_crcx_mdcx_and_rtp_osmux(true);
1383 }
1384
1385 /* create one RTP and one OSmux emulations and pass data in both
1386 directions. Create CRCX with fixed Osmux CID and keep it during
1387 MDCX. This is similar to how BSC sets up the call in AoIP. */
1388 testcase TC_two_crcx_mdcx_and_rtp_osmux_fixed() runs on dummy_CT {
1389 f_two_crcx_mdcx_and_rtp_osmux(false);
1390 }
1391
Harald Welte646ecdb2017-12-28 03:21:57 +01001392 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
1393 var template MgcpCommand cmd;
1394 var MgcpResponse resp;
1395
1396 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1397 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1398
1399 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
1400
1401 setverdict(pass);
1402 }
1403
1404 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
1405 var MgcpEndpoint ep;
1406 var MgcpCallId call_id;
1407 var integer ep_nr;
1408
1409 f_init();
1410
1411 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001412 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
Harald Welte646ecdb2017-12-28 03:21:57 +01001413 call_id := int2hex(ep_nr, 2) & '1234'H;
1414 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
1415 }
1416 }
1417
Harald Welte79181ff2017-11-18 19:26:11 +01001418 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
1419 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +08001420 var template MgcpCommand cmd;
1421 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001422 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001423 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +08001424
Harald Welteedc45c12017-11-18 19:15:05 +01001425 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001426
Harald Welteba62c8c2017-11-18 18:26:49 +01001427 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +01001428 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001429
Harald Welteba62c8c2017-11-18 18:26:49 +01001430 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +08001431
1432 setverdict(pass);
1433 }
1434
Harald Welte79181ff2017-11-18 19:26:11 +01001435 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
1436 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
1437 var template MgcpCommand cmd;
1438 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001439 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001440 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +01001441
1442 f_init(ep);
1443
1444 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1445 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1446
1447 f_dlcx_ok(ep);
1448
1449 setverdict(pass);
1450 }
1451
1452
Harald Welte6d167f82017-11-18 19:41:35 +01001453 /* CRCX + DLCX of valid endpoint but invalid call-id */
1454 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
1455 var template MgcpCommand cmd;
1456 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001457 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001458 var MgcpCallId call_id := '51231'H;
1459
1460 f_init(ep);
1461
1462 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1463 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1464
1465 f_dlcx(ep, "516", *, 'ffff'H);
1466
1467 setverdict(pass);
1468 }
1469
1470
1471 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
1472 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
1473 var template MgcpCommand cmd;
1474 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001475 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +01001476 var MgcpCallId call_id := '51230'H;
1477
1478 f_init(ep);
1479
1480 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1481 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1482
1483 f_dlcx(ep, "515", *, call_id, 'ffff'H);
1484
1485 setverdict(pass);
1486 }
1487
1488
Harald Weltee636afd2017-09-17 16:24:09 +08001489 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +01001490 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
1491 var template MgcpCommand cmd;
1492 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001493 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +01001494 var MgcpCallId call_id := '51229'H;
1495 var template MgcpResponse rtmpl := {
1496 line := {
1497 code := "200",
1498 string := "OK"
1499 },
1500 params:= { },
1501 sdp := omit
1502 };
1503
1504 f_init(ep);
1505
1506 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
1507 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
1508
1509 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
1510 resp := mgcp_transceive_mgw(cmd, rtmpl);
1511 resp := mgcp_transceive_mgw(cmd, rtmpl);
1512
1513 setverdict(pass);
1514 }
1515
Harald Weltebb7523b2018-03-29 08:52:01 +02001516 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
1517 charstring codec) := {
1518 em := {
1519 hostname := host_a,
1520 portnr := omit
1521 },
1522 mgw := {
1523 hostname := host_b,
1524 portnr := omit
1525 },
1526 pt := pt,
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02001527 codec := codec,
1528 osmux_cid_sent := false
Harald Weltebb7523b2018-03-29 08:52:01 +02001529 }
Harald Weltef53f1642017-11-18 19:57:11 +01001530
Harald Weltebb7523b2018-03-29 08:52:01 +02001531 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
1532 testcase TC_rtpem_selftest() runs on dummy_CT {
1533 var RtpemStats stats[2];
1534 var integer local_port := 10000;
1535 var integer local_port2 := 20000;
1536
1537 f_init();
1538
1539 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
1540 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
1541
1542 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
1543 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
1544
1545 log("=== starting");
1546 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1547 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1548
1549 f_sleep(5.0);
1550
1551 log("=== stopping");
1552 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1553 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1554 f_sleep(0.5);
1555 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
1556 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
1557
1558 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1559 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1560 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1561 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001562 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001563 }
1564 setverdict(pass);
1565 }
1566
Philipp Maier2321ef92018-06-27 17:52:04 +02001567 /* Create one half open connection in receive-only mode. The MGW must accept
1568 * the packets but must not send any. */
1569 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
1570 var RtpFlowData flow;
1571 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1572 var MgcpCallId call_id := '1225'H;
1573 var RtpemStats stats;
1574
1575 f_init(ep);
1576 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
1577 flow.em.portnr := 10000;
1578 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, false);
1579
1580 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1581 f_sleep(1.0);
1582 f_flow_delete(RTPEM[0], ep, call_id);
1583
1584 stats := f_rtpem_stats_get(RTPEM[0]);
1585
1586 if (stats.num_pkts_tx < 40) {
1587 setverdict(fail);
1588 }
1589 if (stats.bytes_payload_tx < 190) {
1590 setverdict(fail);
1591 }
Philipp Maier36291392018-07-25 09:40:44 +02001592
1593 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001594
1595 setverdict(pass);
1596 }
1597
1598 /* Create one connection in loopback mode, test if the RTP packets are
1599 * actually reflected */
1600 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
1601 var RtpFlowData flow;
1602 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1603 var MgcpCallId call_id := '1225'H;
1604 var RtpemStats stats;
1605
1606 f_init(ep);
1607 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1608 flow.em.portnr := 10000;
1609 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
1610
1611 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1612 f_sleep(1.0);
1613 f_flow_delete(RTPEM[0], ep, call_id);
1614
1615 stats := f_rtpem_stats_get(RTPEM[0]);
1616
1617 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1618 setverdict(fail);
1619 }
1620 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1621 setverdict(fail);
1622 }
Philipp Maier36291392018-07-25 09:40:44 +02001623
1624 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001625
1626 setverdict(pass);
1627 }
1628
Philipp Maier7df85f62018-07-25 10:26:09 +02001629 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1630 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001631 var RtpFlowData flow[2];
1632 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001633 var MgcpResponse resp;
1634 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1635 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001636 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001637
1638 f_init(ep);
1639
1640 /* from us to MGW */
Philipp Maier7df85f62018-07-25 10:26:09 +02001641 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001642 /* bind local RTP emulation sockets */
1643 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001644 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001645
1646 /* from MGW back to us */
Philipp Maier7df85f62018-07-25 10:26:09 +02001647 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001648 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001649 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1650
1651 if (bidir) {
1652 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1653 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1654
1655 /* Note: When we test bidirectional we may
1656 * loose packets during switch off because
1657 * both ends are transmitting and we only
1658 * can switch them off one by one. */
1659 tolerance := 3;
1660 } else {
1661 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1662 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1663 }
1664
1665 f_sleep(1.0);
1666
1667 f_flow_delete(RTPEM[1]);
1668 f_flow_delete(RTPEM[0], ep, call_id);
1669
1670 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1671 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1672 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1673 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001674 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001675 }
1676
Philipp Maier36291392018-07-25 09:40:44 +02001677 f_rtpem_stats_err_check(stats[0]);
1678 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001679
Philipp Maier2321ef92018-06-27 17:52:04 +02001680 setverdict(pass);
1681 }
1682
1683 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1684 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001685 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001686 }
1687
1688 /* create two local RTP emulations; create two connections on MGW EP,
1689 * exchange some data in both directions */
1690 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001691 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1692 }
1693
1694 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1695 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1696 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1697 }
1698
1699 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1700 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1701 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001702 }
1703
1704 /* create two local RTP emulations and pass data in both directions */
1705 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1706 var RtpFlowData flow[2];
1707 var RtpemStats stats[2];
1708 var MgcpResponse resp;
1709 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1710 var MgcpCallId call_id := '1227'H;
1711 var integer num_pkts_tx[2];
1712 var integer temp;
1713
1714 f_init(ep);
1715
1716 /* Create the first connection in receive only mode */
1717 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1718 flow[0].em.portnr := 10000;
1719 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1720
1721 /* Create the second connection. This connection will be also
1722 * in receive only mode */
1723 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1724 flow[1].em.portnr := 20000;
1725 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], false);
1726
1727 /* The first leg starts transmitting */
1728 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1729 f_sleep(0.5);
1730 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1731 if (stats[0].num_pkts_rx_err_disabled != 0) {
1732 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001733 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001734 }
1735 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1736 if (stats[1].num_pkts_rx_err_disabled != 0) {
1737 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001738 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001739 }
1740
1741 /* The second leg starts transmitting a little later */
1742 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1743 f_sleep(1.0);
1744 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1745 if (stats[0].num_pkts_rx_err_disabled != 0) {
1746 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001747 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001748 }
1749 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1750 if (stats[1].num_pkts_rx_err_disabled != 0) {
1751 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001752 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001753 }
1754
1755 /* The first leg will now be switched into bidirectional
1756 * mode, but we do not expect any data comming back yet. */
1757 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1758 f_sleep(0.5);
1759 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1760 if (stats[1].num_pkts_rx_err_disabled != 0) {
1761 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001762 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001763 }
1764 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1765 if (stats[1].num_pkts_rx_err_disabled != 0) {
1766 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001767 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001768 }
1769
1770 /* When the second leg is switched into bidirectional mode
1771 * as well, then the MGW will connect the two together and
1772 * we should see RTP streams passing through from both ends. */
1773 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1774 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1775 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1776 num_pkts_tx[0] := stats[0].num_pkts_tx
1777 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1778 num_pkts_tx[1] := stats[1].num_pkts_tx
1779
1780 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1781 f_sleep(2.0);
1782
1783 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1784 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1785
1786 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1787 if (temp > 3 or temp < -3) {
1788 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001789 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001790 }
1791
1792 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1793 if (temp > 3 or temp < -3) {
1794 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001795 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001796 }
Philipp Maier36291392018-07-25 09:40:44 +02001797
1798 f_rtpem_stats_err_check(stats[0]);
1799 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001800
1801 /* Tear down */
1802 f_flow_delete(RTPEM[0]);
1803 f_flow_delete(RTPEM[1], ep, call_id);
1804 setverdict(pass);
1805 }
1806
1807 /* Test what happens when two RTP streams from different sources target
1808 * a single connection. Is the unsolicited stream properly ignored? */
1809 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1810 var RtpFlowData flow[2];
1811 var RtpemStats stats[2];
1812 var MgcpResponse resp;
1813 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1814 var MgcpCallId call_id := '1234321326'H;
1815 var integer unsolicited_port := 10002;
1816
1817 f_init(ep);
1818
1819 /* from us to MGW */
1820 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1821 /* bind local RTP emulation sockets */
1822 flow[0].em.portnr := 10000;
1823 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1824
1825 /* from MGW back to us */
1826 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1827 flow[1].em.portnr := 20000;
1828 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001829
1830 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1831 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1832
Philipp Maier2321ef92018-06-27 17:52:04 +02001833 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001834
Philipp Maier2321ef92018-06-27 17:52:04 +02001835 /* Start inserting unsolicited RTP packets */
Philipp Maier4de3e372018-06-29 17:15:22 +02001836 f_rtpem_bind(RTPEM[2], mp_local_ip, unsolicited_port);
1837 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001838 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1839
1840 f_sleep(0.5);
1841
Daniel Willmanna069d382018-12-13 13:53:33 +01001842 /* Stop transmitting packets and tear down the flows */
1843 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02001844 f_flow_delete(RTPEM[0]);
1845 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001846
1847 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1848 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1849 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1850 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001851 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001852 }
1853
Philipp Maier36291392018-07-25 09:40:44 +02001854 f_rtpem_stats_err_check(stats[0]);
1855 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02001856
Harald Weltebb7523b2018-03-29 08:52:01 +02001857 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001858 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001859
Philipp Maier2321ef92018-06-27 17:52:04 +02001860 /* Test a handover situation. We first create two connections transmit
1861 * some data bidirectionally. Then we will simulate a handover situation. */
1862 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1863 var RtpFlowData flow[2];
1864 var RtpemStats stats[3];
1865 var MgcpResponse resp;
1866 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1867 var MgcpCallId call_id := '76338'H;
1868 var integer port_old;
1869
1870 f_init(ep);
1871
1872 /* First connection (BTS) */
1873 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1874 /* bind local RTP emulation sockets */
1875 flow[0].em.portnr := 10000;
1876 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1877
1878 /* Second connection (PBX) */
1879 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1880 flow[1].em.portnr := 20000;
1881 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1882
1883 /* Normal rtp flow for one second */
1884 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1885 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1886 f_sleep(1.0);
1887
1888 /* Now switch the flow over to a new port (BTS) */
1889 port_old := flow[0].em.portnr;
1890 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001891 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02001892 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001893 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02001894
1895 /* When handing over a call, the old source may still keep
1896 * transmitting for a while. We simulate this by injecting
1897 * some unsolicited packets on the behalf of the old source,
1898 * (old remote port) */
Philipp Maier4de3e372018-06-29 17:15:22 +02001899 f_rtpem_bind(RTPEM[2], mp_local_ip, port_old);
1900 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001901 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1902 f_sleep(1.0);
1903 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
1904 f_sleep(1.0);
1905
1906 /* Terminate call */
1907 f_flow_delete(RTPEM[0]);
1908 f_flow_delete(RTPEM[1], ep, call_id);
1909
1910 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1911 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1912 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
1913 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001914 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001915 }
1916 stats[2] := f_rtpem_stats_get(RTPEM[2]);
1917 if (stats[2].num_pkts_rx_err_disabled != 0) {
1918 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02001919 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001920 }
1921
Philipp Maier36291392018-07-25 09:40:44 +02001922 f_rtpem_stats_err_check(stats[0]);
1923 f_rtpem_stats_err_check(stats[1]);
1924 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02001925
Philipp Maier2321ef92018-06-27 17:52:04 +02001926 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02001927 }
Harald Weltef53f1642017-11-18 19:57:11 +01001928
Philipp Maier6d4e0942019-02-21 17:35:01 +01001929
1930 /* create two local RTP emulations; create two connections on MGW EP, see if
1931 * exchanged data is converted bwtween ts101318 and rfc5993 */
1932 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
1933 var RtpFlowData flow[2];
1934 var RtpemStats stats[2];
1935 var MgcpResponse resp;
1936 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1937 var MgcpCallId call_id := '1226'H;
1938
1939 f_init(ep);
1940
1941 /* Turn on conversion mode */
1942 f_vty_enter_config(MGWVTY);
1943 f_vty_transceive(MGWVTY, "mgcp");
1944 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
1945
1946 /* from us to MGW */
1947 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1948 /* bind local RTP emulation sockets */
1949 flow[0].em.portnr := 10000;
1950 flow[0].rtp_cfg := c_RtpemDefaultCfg;
1951 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1952 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1953 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1954 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1955
1956 /* from MGW back to us */
1957 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1958 flow[1].em.portnr := 20000;
1959 flow[1].rtp_cfg := c_RtpemDefaultCfg;
1960 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
1961 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1962 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1963 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1964
1965 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1966 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1967
1968 f_sleep(1.0);
1969
1970 f_flow_delete(RTPEM[0]);
1971 f_flow_delete(RTPEM[1], ep, call_id);
1972
1973 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1974 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1975
1976 f_rtpem_stats_err_check(stats[0]);
1977 f_rtpem_stats_err_check(stats[1]);
1978
1979 /* Turn off conversion mode */
1980 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
1981
1982 setverdict(pass);
1983 }
1984
Philipp Maier4f764ce2019-03-07 10:54:10 +01001985 /* create two local RTP emulations; create two connections on MGW EP, see if
1986 * exchanged data is converted between AMR octet-aligned and bandwith
1987 * efficient-mode */
1988 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
1989 var RtpFlowData flow[2];
1990 var RtpemStats stats[2];
1991 var MgcpResponse resp;
1992 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1993 var MgcpCallId call_id := '1226'H;
1994
1995 f_init(ep);
1996
1997 /* from us to MGW */
1998 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1999 /* bind local RTP emulation sockets */
2000 flow[0].em.portnr := 10000;
2001 flow[0].rtp_cfg := c_RtpemDefaultCfg;
2002 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
2003 flow[0].rtp_cfg.rx_fixed_payload := pl0;
2004 flow[0].rtp_cfg.tx_fixed_payload := pl0;
2005 flow[0].fmtp := fmtp0;
2006 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
2007
2008 /* from MGW back to us */
2009 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
2010 flow[1].em.portnr := 20000;
2011 flow[1].rtp_cfg := c_RtpemDefaultCfg;
2012 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
2013 flow[1].rtp_cfg.rx_fixed_payload := pl1;
2014 flow[1].rtp_cfg.tx_fixed_payload := pl1;
2015 flow[1].fmtp := fmtp1;
2016 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
2017
2018 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
2019 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
2020
2021 f_sleep(1.0);
2022
2023 f_flow_delete(RTPEM[0]);
2024 f_flow_delete(RTPEM[1], ep, call_id);
2025
2026 stats[0] := f_rtpem_stats_get(RTPEM[0]);
2027 stats[1] := f_rtpem_stats_get(RTPEM[1]);
2028
2029 f_rtpem_stats_err_check(stats[0]);
2030 f_rtpem_stats_err_check(stats[1]);
2031
2032 setverdict(pass);
2033 }
2034
2035 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
2036 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=1", "octet-align=0");
2037 }
2038
2039 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
2040 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
2041 }
2042
2043 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
2044 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
2045 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01002046
Harald Weltee636afd2017-09-17 16:24:09 +08002047 /* TODO: Double-DLCX (no retransmission) */
2048
2049
2050
2051 /* TODO: AUEP (various) */
2052 /* TODO: RSIP (various) */
2053 /* TODO: RQNT (various) */
2054 /* TODO: EPCF (various) */
2055 /* TODO: AUCX (various) */
2056 /* TODO: invalid verb (various) */
2057
Harald Welte00a067f2017-09-13 23:27:17 +02002058 control {
2059 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08002060 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02002061 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01002062 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08002063 execute(TC_crcx_unsupp_mode());
2064 execute(TC_crcx_early_bidir_mode());
2065 execute(TC_crcx_unsupp_param());
2066 execute(TC_crcx_missing_callid());
2067 execute(TC_crcx_missing_mode());
2068 execute(TC_crcx_unsupp_packet_intv());
2069 execute(TC_crcx_illegal_double_lco());
2070 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01002071 execute(TC_crcx_wildcarded());
2072 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08002073 execute(TC_mdcx_without_crcx());
2074 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01002075 execute(TC_mdcx_wildcarded());
2076 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01002077 execute(TC_crcx_and_dlcx_ep_callid_connid());
2078 execute(TC_crcx_and_dlcx_ep_callid());
2079 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01002080 execute(TC_crcx_and_dlcx_ep_callid_inval());
2081 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01002082 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01002083
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +02002084 execute(TC_crcx_osmux_wildcard());
2085 execute(TC_crcx_osmux_fixed());
2086 execute(TC_crcx_osmux_fixed_twice());
2087 execute(TC_one_crcx_receive_only_osmux());
2088 execute(TC_one_crcx_loopback_osmux());
2089 execute(TC_two_crcx_and_rtp_osmux());
2090 execute(TC_two_crcx_and_rtp_osmux_bidir());
2091 execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard());
2092 execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed());
2093
Harald Welte33d82162017-12-28 03:21:57 +01002094 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02002095
2096 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02002097
2098 execute(TC_one_crcx_receive_only_rtp());
2099 execute(TC_one_crcx_loopback_rtp());
Harald Weltebb7523b2018-03-29 08:52:01 +02002100 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02002101 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02002102 execute(TC_two_crcx_diff_pt_and_rtp());
2103 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02002104 execute(TC_two_crcx_mdcx_and_rtp());
2105 execute(TC_two_crcx_and_unsolicited_rtp());
2106 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01002107 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01002108 execute(TC_amr_oa_bwe_rtp_conversion());
2109 execute(TC_amr_oa_oa_rtp_conversion());
2110 execute(TC_amr_bwe_bwe_rtp_conversion());
Harald Welte00a067f2017-09-13 23:27:17 +02002111 }
2112}