blob: 917ef892050478d1fc34be9cfd6f01c998de0ba4 [file] [log] [blame]
Harald Welte4029e8c2017-11-23 22:00:42 +01001module MGCP_Templates {
2
Harald Welte35bb7162018-01-03 21:07:52 +01003/* MGCP Templates, building on top of MGCP_Types (Osmocom) and SDP_Types from Ericsson.
4 *
5 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
6 * All rights reserved.
7 *
8 * Released under the terms of GNU General Public License, Version 2 or
9 * (at your option) any later version.
10 */
11
12
Harald Welte4029e8c2017-11-23 22:00:42 +010013 import from MGCP_Types all;
14 import from SDP_Types all;
15
16 function f_mgcp_par_append(inout template MgcpParameterList list, template MgcpParameter par) {
17 var integer len := lengthof(list);
18 list[len] := par;
19 }
20
21 /* 3.2.2.6 Connection Mode (sendonly, recvonly, sendrecv, confrnce, inactive, loopback,
22 * conttest, netwloop, netwtest) */
23 template MgcpParameter t_MgcpParConnMode(template MgcpConnectionMode mode) := { "M", mode };
24
25 /* 3.2.2.2 CallId: maximum 32 hex chars */
26 template MgcpParameter ts_MgcpParCallId(MgcpCallId cid) := {
27 code := "C",
28 val := hex2str(cid)
29 };
30
31 /* 3.2.2.18 RequestIdentifier: Maximum 32 hex chars */
32 template MgcpParameter ts_MgcpParReqId(MgcpRequestId rid) := {
33 code := "X",
34 val := hex2str(rid)
35 };
36
Harald Welte363cb0a2018-01-30 19:35:53 +010037 /* 3.2.1.3 SpecificEndpointId */
38 template MgcpParameter ts_MgcpParSpecEP(MgcpEndpoint ep) := {
39 code := "Z",
40 val := ep
41 };
42
Harald Welte4029e8c2017-11-23 22:00:42 +010043 /* 3.2.2.10: LocalConnectionOptions (codec, packetization, bandwidth, ToS, eco, gain, silence, ...) */
44 template MgcpParameter t_MgcpParLocConnOpt(template charstring lco) := { "L", lco };
45
46 /* 3.2.2.5: ConnectionId: maximum 32 hex chars */
47 template MgcpParameter ts_MgcpParConnectionId(MgcpConnectionId cid) := {
48 code := "I",
49 val := hex2str(cid)
50 };
51
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020052 /* Osmocom extension: X-Osmux: {*,%u} */
53 template MgcpParameter ts_MgcpParOsmuxCID(MgcpOsmuxCID osmux_cid) := {
54 code := "X-OSMUX",
55 val := f_mgcp_osmux_cid_encode(osmux_cid)
56 };
57
Pau Espin Pedrolbefd3aa2020-09-21 10:54:42 +020058 /* Osmocom extension: X-Osmux: {*,%u} */
59 template MgcpParameter t_MgcpParOsmoIGN(template charstring val) := {
60 code := "X-OSMO-IGN",
61 val := val
62 };
63
Harald Welte4029e8c2017-11-23 22:00:42 +010064 /* osmo-bsc_mgcp implements L/C/M/X only, osmo-mgw adds 'I' */
65 /* SDP: osmo-bsc_mgcp implements Tx of v,o,s,c,t,m,a */
66
67 template MgcpResponse tr_MgcpResp_Err(template MgcpResponseCode code) := {
68 line := {
69 code := code,
70 trans_id := ?,
71 string := ?
72 },
73 params := {},
74 sdp := omit
75 }
76
77 template MgcpCommandLine t_MgcpCmdLine(template charstring verb, template MgcpTransId trans_id, template charstring ep) := {
78 verb := verb,
79 trans_id := trans_id,
80 ep := ep,
81 ver := "1.0"
82 };
83
84 template MgcpCommand ts_CRCX(MgcpTransId trans_id, charstring ep, MgcpConnectionMode mode, MgcpCallId call_id, template SDP_Message sdp := omit) := {
85 line := t_MgcpCmdLine("CRCX", trans_id, ep),
86 params := {
87 t_MgcpParConnMode(mode),
88 ts_MgcpParCallId(call_id),
89 //t_MgcpParReqId(omit),
Daniel Willmannfbef7142017-11-30 13:16:14 +010090 t_MgcpParLocConnOpt("p:20, a:AMR")
Harald Welte4029e8c2017-11-23 22:00:42 +010091 },
92 sdp := sdp
93 }
94
Philipp Maier45635f42018-06-05 17:28:02 +020095 template MgcpCommand ts_CRCX_no_lco(MgcpTransId trans_id, charstring ep, MgcpConnectionMode mode, MgcpCallId call_id, template SDP_Message sdp := omit) := {
96 line := t_MgcpCmdLine("CRCX", trans_id, ep),
97 params := {
98 t_MgcpParConnMode(mode),
99 ts_MgcpParCallId(call_id)
100 },
101 sdp := sdp
102 }
103
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200104 template MgcpCommand ts_CRCX_osmux(MgcpTransId trans_id, charstring ep, MgcpConnectionMode mode, MgcpCallId call_id, MgcpOsmuxCID osmux_cid, template SDP_Message sdp := omit) := {
105 line := t_MgcpCmdLine("CRCX", trans_id, ep),
106 params := {
107 t_MgcpParConnMode(mode),
108 ts_MgcpParCallId(call_id),
109 //t_MgcpParReqId(omit),
110 t_MgcpParLocConnOpt("p:20, a:AMR"),
111 ts_MgcpParOsmuxCID(osmux_cid)
112 },
113 sdp := sdp
114 }
115
Harald Welte4017d552018-01-26 21:40:05 +0100116 template MgcpCommand tr_CRCX(template MgcpEndpoint ep := ?) := {
117 line := t_MgcpCmdLine("CRCX", ?, ep),
Harald Welte90029572017-11-24 23:39:50 +0100118 params := *,
119 sdp := *
120 }
121
Harald Welte4029e8c2017-11-23 22:00:42 +0100122 template MgcpResponse tr_CRCX_ACK := {
123 line := {
124 code := "200",
Harald Welte51f34ad2017-11-24 20:40:43 +0100125 trans_id := ?,
Harald Welte4029e8c2017-11-23 22:00:42 +0100126 string := "OK"
127 },
128 params:= { { "I", ? }, *},
129 sdp := ?
130 }
131
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200132 template MgcpResponse tr_CRCX_ACK_osmux := {
133 line := {
134 code := "200",
135 trans_id := ?,
136 string := "OK"
137 },
138 params:= { { "I", ? }, {"X-OSMUX", ?}, *},
139 sdp := ?
140 }
141
Harald Welte90029572017-11-24 23:39:50 +0100142 template MgcpResponse ts_CRCX_ACK(MgcpTransId trans_id, MgcpConnectionId conn_id, template SDP_Message sdp := omit) := {
143 line := {
144 code := "200",
145 trans_id := trans_id,
146 string := "OK"
147 },
148 params:= { ts_MgcpParConnectionId(conn_id) },
149 sdp := sdp
150 }
151
Pau Espin Pedrolc6a53db2019-05-20 19:31:47 +0200152 template MgcpResponse ts_CRCX_ACK_osmux(MgcpTransId trans_id, MgcpConnectionId conn_id, MgcpOsmuxCID osmux_cid, template SDP_Message sdp := omit) := {
153 line := {
154 code := "200",
155 trans_id := trans_id,
156 string := "OK"
157 },
158 params:= {
159 ts_MgcpParConnectionId(conn_id),
160 ts_MgcpParOsmuxCID(osmux_cid)
161 },
162 sdp := sdp
163 }
164
Harald Welte4029e8c2017-11-23 22:00:42 +0100165 template MgcpCommand ts_MDCX(MgcpTransId trans_id, charstring ep, MgcpConnectionMode mode, MgcpCallId call_id, MgcpConnectionId conn_id, template SDP_Message sdp := omit) := {
166 line := t_MgcpCmdLine("MDCX", trans_id, ep),
167 params := {
168 t_MgcpParConnMode(mode),
169 ts_MgcpParCallId(call_id),
170 ts_MgcpParConnectionId(conn_id),
171 //t_MgcpParReqId(omit),
Daniel Willmannfbef7142017-11-30 13:16:14 +0100172 t_MgcpParLocConnOpt("p:20, a:AMR")
Harald Welte4029e8c2017-11-23 22:00:42 +0100173 },
174 sdp := sdp
175 }
176
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200177 template MgcpCommand ts_MDCX_osmux(MgcpTransId trans_id, charstring ep, MgcpConnectionMode mode, MgcpCallId call_id, MgcpConnectionId conn_id, MgcpOsmuxCID osmux_cid, template SDP_Message sdp := omit) := {
178 line := t_MgcpCmdLine("MDCX", trans_id, ep),
179 params := {
180 t_MgcpParConnMode(mode),
181 ts_MgcpParCallId(call_id),
182 ts_MgcpParConnectionId(conn_id),
183 //t_MgcpParReqId(omit),
184 t_MgcpParLocConnOpt("p:20, a:AMR"),
185 ts_MgcpParOsmuxCID(osmux_cid)
186 },
187 sdp := sdp
188 }
189
Harald Welte90029572017-11-24 23:39:50 +0100190 template MgcpCommand tr_MDCX := {
191 line := t_MgcpCmdLine("MDCX", ?, ?),
192 params := *,
193 sdp := *
194 }
195
Harald Weltebb7523b2018-03-29 08:52:01 +0200196 template MgcpResponse tr_MDCX_ACK := {
197 line := {
198 code := "200",
199 trans_id := ?,
200 string := "OK"
201 },
202 params := *,
203 sdp := ?
204 }
205
Harald Welte90029572017-11-24 23:39:50 +0100206 template MgcpResponse ts_MDCX_ACK(MgcpTransId trans_id, MgcpConnectionId conn_id, template SDP_Message sdp := omit) := ts_CRCX_ACK(trans_id, conn_id, sdp);
Pau Espin Pedrola65697d2019-05-21 12:54:39 +0200207 template MgcpResponse ts_MDCX_ACK_osmux(MgcpTransId trans_id, MgcpConnectionId conn_id, MgcpOsmuxCID osmux_cid, template SDP_Message sdp := omit) := ts_CRCX_ACK_osmux(trans_id, conn_id, osmux_cid, sdp);
Harald Welte90029572017-11-24 23:39:50 +0100208
Harald Welte4029e8c2017-11-23 22:00:42 +0100209 /* have a function that generates a template, rather than a template in order to handle
210 * optional parameters */
211 function ts_DLCX(MgcpTransId trans_id, charstring ep, template MgcpCallId call_id := omit,
212 template MgcpConnectionId conn_id := omit) return template MgcpCommand {
213 var template MgcpCommand cmd;
214 cmd.line := t_MgcpCmdLine("DLCX", trans_id, ep);
215 cmd.params := {};
216 cmd.sdp := omit;
217 if (isvalue(call_id)) {
218 f_mgcp_par_append(cmd.params, ts_MgcpParCallId(valueof(call_id)));
219 if (isvalue(conn_id)) {
220 f_mgcp_par_append(cmd.params, ts_MgcpParConnectionId(valueof(conn_id)));
221 }
222 }
223 return cmd;
224 }
225
Harald Welteb71901a2018-01-26 19:16:05 +0100226 template MgcpCommand tr_DLCX(template MgcpEndpoint ep := ?) := {
227 line := t_MgcpCmdLine("DLCX", ?, ep),
Harald Welte90029572017-11-24 23:39:50 +0100228 params := *,
229 sdp := *
230 }
231
Harald Weltec82eef42017-11-24 20:40:12 +0100232 template MgcpResponse tr_DLCX_ACK := {
233 line := {
Daniel Willmann961e5c92017-11-30 16:37:40 +0100234 code := ("200", "250"),
Harald Weltec82eef42017-11-24 20:40:12 +0100235 trans_id := ?,
236 string := "OK"
237 },
238 params:= *,
239 sdp := *
240 }
241
Harald Welteb71901a2018-01-26 19:16:05 +0100242 template MgcpResponse ts_DLCX_ACK2(MgcpTransId trans_id) := {
243 line := {
244 code := "250",
245 trans_id := trans_id,
246 string := "OK"
247 },
248 params:= { /* list of ConnectionIDs */ },
249 sdp := omit
250 }
251
252
253
Harald Welte90029572017-11-24 23:39:50 +0100254 template MgcpResponse ts_DLCX_ACK(MgcpTransId trans_id, MgcpConnectionId conn_id, template SDP_Message sdp := omit) := ts_CRCX_ACK(trans_id, conn_id, sdp);
255
Harald Weltee98bb2e2017-11-29 12:09:48 +0100256 template MgcpCommand tr_RSIP := {
257 line := t_MgcpCmdLine("RSIP", ?, ?),
258 params := *,
259 sdp := *
260 }
261
Harald Welte4029e8c2017-11-23 22:00:42 +0100262 /* SDP Templates */
263 template SDP_Origin ts_SDP_origin(charstring addr, charstring session_id,
264 charstring session_version := "1",
265 charstring addr_type := "IP4",
266 charstring user_name := "-") := {
267 user_name := user_name,
268 session_id := session_id,
269 session_version := session_version,
270 net_type := "IN",
271 addr_type := addr_type,
272 addr := addr
273 }
274
275 template SDP_connection ts_SDP_connection_IP(charstring addr, charstring addr_type := "IP4",
276 template integer ttl := omit,
277 template integer num_of_addr := omit) :={
278 net_type := "IN",
279 addr_type := addr_type,
280 conn_addr := {
281 addr := addr,
282 ttl := ttl,
283 num_of_addr := num_of_addr
284 }
285 }
286
287 template SDP_time ts_SDP_time(charstring beg, charstring end) := {
288 time_field := {
289 start_time := beg,
290 stop_time := end
291 },
292 time_repeat := omit
293 }
294
295 template SDP_media_desc ts_SDP_media_desc(integer port_number, SDP_fmt_list fmts,
296 SDP_attribute_list attributes) := {
297 media_field := {
298 media := "audio",
299 ports := {
300 port_number := port_number,
301 num_of_ports := omit
302 },
303 transport := "RTP/AVP",
304 fmts := fmts
305 },
306 information := omit,
307 connections := omit,
308 bandwidth := omit,
309 key := omit,
310 attributes := attributes
311 }
312
313 /* master template for generating SDP based in template arguments */
314 template SDP_Message ts_SDP(charstring local_addr, charstring remote_addr,
315 charstring session_id, charstring session_version,
316 integer rtp_port, SDP_fmt_list fmts,
317 SDP_attribute_list attributes) := {
318 protocol_version := 0,
Pau Espin Pedrol384e9492020-09-03 17:05:19 +0200319 origin := ts_SDP_origin(local_addr, session_id, session_version, f_mgcp_addr2addrtype(local_addr)),
Harald Welte4029e8c2017-11-23 22:00:42 +0100320 session_name := "-",
321 information := omit,
322 uri := omit,
323 emails := omit,
324 phone_numbers := omit,
Pau Espin Pedrol384e9492020-09-03 17:05:19 +0200325 connection := ts_SDP_connection_IP(remote_addr, f_mgcp_addr2addrtype(remote_addr)),
Harald Welte4029e8c2017-11-23 22:00:42 +0100326 bandwidth := omit,
327 times := { ts_SDP_time("0","0") },
328 timezone_adjustments := omit,
329 key := omit,
330 attributes := omit,
331 media_list := { ts_SDP_media_desc(rtp_port, fmts, attributes) }
332 }
333
334 template SDP_attribute ts_SDP_rtpmap(integer fmt, charstring val) := {
335 rtpmap := {
336 attr_value := int2str(fmt) & " " & val
337 }
338 }
339 template SDP_attribute ts_SDP_ptime(integer p) := {
340 ptime := {
341 attr_value := int2str(p)
342 }
343 }
Philipp Maierc8c0b402019-03-07 10:48:45 +0100344 template SDP_attribute ts_SDP_fmtp(integer fmt, charstring val) := {
345 fmtp := {
346 attr_value := int2str(fmt) & " " & val
347 }
348 }
Harald Welte4029e8c2017-11-23 22:00:42 +0100349
Pau Espin Pedrol384e9492020-09-03 17:05:19 +0200350 function f_mgcp_addr2addrtype(charstring addr) return charstring {
351 for (var integer i := 0; i < lengthof(addr); i := i + 1) {
352 if (addr[i] == ":") {
353 return "IP6";
354 }
355 }
356 return "IP4";
357 }
358
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200359 /* -1 is wildcard, positive is translated as string */
360 function f_mgcp_osmux_cid_encode(MgcpOsmuxCID osmux_cid) return charstring {
361 if (osmux_cid == -1) {
362 return "*";
363 }
364 return int2str(osmux_cid);
365 }
366
367 function f_mgcp_osmux_cid_decode(charstring osmux_cid) return MgcpOsmuxCID {
368 if (osmux_cid == "*") {
369 return -1;
370 }
371 return str2int(osmux_cid);
372 }
373
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100374 function f_mgcp_contains_par(MgcpMessage msg, MgcpInfoCode code) return boolean {
375 var MgcpParameterList pars;
376 if (ischosen(msg.command)) {
377 pars := msg.command.params;
378 } else {
379 pars := msg.response.params;
380 }
381 for (var integer i := 0; i < lengthof(pars); i := i + 1) {
382 var MgcpParameter par := pars[i];
383 if (par.code == code) {
384 return true;
385 }
386 }
387 return false;
388 }
389
Harald Welteb71901a2018-01-26 19:16:05 +0100390 function f_mgcp_extract_par(MgcpMessage msg, MgcpInfoCode code) return charstring {
391 var MgcpParameterList pars;
392 if (ischosen(msg.command)) {
393 pars := msg.command.params;
394 } else {
395 pars := msg.response.params;
396 }
397 for (var integer i := 0; i < lengthof(pars); i := i + 1) {
398 var MgcpParameter par := pars[i];
399 if (par.code == code) {
400 return par.val;
Harald Welte4c11d562017-11-24 23:39:00 +0100401 }
402 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200403 setverdict(fail, "Could not extract parameters for code ", code);
Harald Welteb71901a2018-01-26 19:16:05 +0100404 return "";
405 }
406
Harald Welte1fe74812018-01-29 21:57:26 +0100407 function f_MgcpResp_extract_par(MgcpResponse resp, MgcpInfoCode code) return charstring {
Harald Welteb71901a2018-01-26 19:16:05 +0100408 var MgcpMessage msg := {
409 response := resp
410 }
Harald Welte1fe74812018-01-29 21:57:26 +0100411 return f_mgcp_extract_par(msg, code);
Harald Welteb71901a2018-01-26 19:16:05 +0100412 }
413
Harald Welte1fe74812018-01-29 21:57:26 +0100414 function f_MgcpCmd_extract_par(MgcpCommand cmd, MgcpInfoCode code) return charstring {
Harald Welteb71901a2018-01-26 19:16:05 +0100415 var MgcpMessage msg := {
416 command := cmd
417 }
Harald Welte1fe74812018-01-29 21:57:26 +0100418 return f_mgcp_extract_par(msg, code);
Harald Welte4c11d562017-11-24 23:39:00 +0100419 }
420
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100421 function f_MgcpCmd_contains_par(MgcpCommand cmd, MgcpInfoCode code) return boolean {
422 var MgcpMessage msg := {
423 command := cmd
424 }
425 return f_mgcp_contains_par(msg, code);
426 }
427
Harald Welte1fe74812018-01-29 21:57:26 +0100428 function f_MgcpResp_extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
429 return str2hex(f_MgcpResp_extract_par(resp, "I"));
430 }
431
432 function f_MgcpCmd_extract_call_id(MgcpCommand cmd) return MgcpCallId {
433 return str2hex(f_MgcpCmd_extract_par(cmd, "C"));
434 }
435
436 function f_MgcpCmd_extract_conn_id(MgcpCommand cmd) return MgcpConnectionId {
437 return str2hex(f_MgcpCmd_extract_par(cmd, "I"));
438 }
439
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200440 function f_MgcpCmd_extract_osmux_cid(MgcpCommand cmd) return MgcpOsmuxCID {
441 return f_mgcp_osmux_cid_decode(f_MgcpCmd_extract_par(cmd, "X-OSMUX"));
442 }
443
Harald Welte1fe74812018-01-29 21:57:26 +0100444
Harald Welte4c11d562017-11-24 23:39:00 +0100445 function f_mgcp_alloc_tid() return MgcpTransId {
446 return int2str(float2int(rnd()*2147483647.0));
447 }
448
449 function f_mgcp_alloc_call_id() return MgcpCallId {
450 return int2hex(float2int(rnd()*2147483647.0), 8);
451 }
452
453 function f_mgcp_alloc_conn_id() return MgcpConnectionId {
454 return int2hex(float2int(rnd()*2147483647.0), 8);
455 }
456
Harald Weltebb5a1212018-01-26 10:34:44 +0100457 /* those verbs that related to a connection (and hence have ConnectionId) */
458 template MgcpVerb tr_MgcpVerb_ConnectionOriented := ("CRCX", "MDCX", "DLCX", "AUCX");
459 /* entire command template matching only connection oriented verbs */
460 template MgcpCommand tr_MgcpCommand_CO := {
461 line := {
462 verb := tr_MgcpVerb_ConnectionOriented,
463 trans_id := ?,
464 ep := ?,
465 ver := ?
466 },
467 params := *,
468 sdp := *
469 }
470
Neels Hofmeyrac7526d2019-10-15 16:54:37 +0200471 function f_mgcp_find_param_entry(MgcpParameterList pars, MgcpInfoCode code, out charstring ret)
472 return boolean {
473 for (var integer i := 0; i < sizeof(pars); i := i+1) {
474 if (pars[i].code == code) {
475 ret := pars[i].val;
476 return true;
477 }
478 }
479 return false;
480 }
481
Harald Welte363cb0a2018-01-30 19:35:53 +0100482 function f_mgcp_find_param(MgcpMessage msg, MgcpInfoCode code, out charstring ret)
483 return boolean {
484 var MgcpParameterList pars;
485 if (ischosen(msg.command)) {
486 pars := msg.command.params;
487 } else {
488 pars := msg.response.params;
489 }
Neels Hofmeyrac7526d2019-10-15 16:54:37 +0200490 return f_mgcp_find_param_entry(pars, code, ret);
Harald Welte363cb0a2018-01-30 19:35:53 +0100491 }
492
493 /* template to determine if a MGCP endpoint is a wildcard endpoint */
494 template charstring t_MGCP_EP_wildcard := (pattern "\*@*", pattern "rtpbridge/\*@*");
495
Harald Welteb71901a2018-01-26 19:16:05 +0100496
Harald Welte4029e8c2017-11-23 22:00:42 +0100497}