blob: 4cc2c6b336ad12a9225164480935c9be274dc651 [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
Harald Welte4029e8c2017-11-23 22:00:42 +010058 /* osmo-bsc_mgcp implements L/C/M/X only, osmo-mgw adds 'I' */
59 /* SDP: osmo-bsc_mgcp implements Tx of v,o,s,c,t,m,a */
60
61 template MgcpResponse tr_MgcpResp_Err(template MgcpResponseCode code) := {
62 line := {
63 code := code,
64 trans_id := ?,
65 string := ?
66 },
67 params := {},
68 sdp := omit
69 }
70
71 template MgcpCommandLine t_MgcpCmdLine(template charstring verb, template MgcpTransId trans_id, template charstring ep) := {
72 verb := verb,
73 trans_id := trans_id,
74 ep := ep,
75 ver := "1.0"
76 };
77
78 template MgcpCommand ts_CRCX(MgcpTransId trans_id, charstring ep, MgcpConnectionMode mode, MgcpCallId call_id, template SDP_Message sdp := omit) := {
79 line := t_MgcpCmdLine("CRCX", trans_id, ep),
80 params := {
81 t_MgcpParConnMode(mode),
82 ts_MgcpParCallId(call_id),
83 //t_MgcpParReqId(omit),
Daniel Willmannfbef7142017-11-30 13:16:14 +010084 t_MgcpParLocConnOpt("p:20, a:AMR")
Harald Welte4029e8c2017-11-23 22:00:42 +010085 },
86 sdp := sdp
87 }
88
Philipp Maier45635f42018-06-05 17:28:02 +020089 template MgcpCommand ts_CRCX_no_lco(MgcpTransId trans_id, charstring ep, MgcpConnectionMode mode, MgcpCallId call_id, template SDP_Message sdp := omit) := {
90 line := t_MgcpCmdLine("CRCX", trans_id, ep),
91 params := {
92 t_MgcpParConnMode(mode),
93 ts_MgcpParCallId(call_id)
94 },
95 sdp := sdp
96 }
97
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +020098 template MgcpCommand ts_CRCX_osmux(MgcpTransId trans_id, charstring ep, MgcpConnectionMode mode, MgcpCallId call_id, MgcpOsmuxCID osmux_cid, template SDP_Message sdp := omit) := {
99 line := t_MgcpCmdLine("CRCX", trans_id, ep),
100 params := {
101 t_MgcpParConnMode(mode),
102 ts_MgcpParCallId(call_id),
103 //t_MgcpParReqId(omit),
104 t_MgcpParLocConnOpt("p:20, a:AMR"),
105 ts_MgcpParOsmuxCID(osmux_cid)
106 },
107 sdp := sdp
108 }
109
Harald Welte4017d552018-01-26 21:40:05 +0100110 template MgcpCommand tr_CRCX(template MgcpEndpoint ep := ?) := {
111 line := t_MgcpCmdLine("CRCX", ?, ep),
Harald Welte90029572017-11-24 23:39:50 +0100112 params := *,
113 sdp := *
114 }
115
Harald Welte4029e8c2017-11-23 22:00:42 +0100116 template MgcpResponse tr_CRCX_ACK := {
117 line := {
118 code := "200",
Harald Welte51f34ad2017-11-24 20:40:43 +0100119 trans_id := ?,
Harald Welte4029e8c2017-11-23 22:00:42 +0100120 string := "OK"
121 },
122 params:= { { "I", ? }, *},
123 sdp := ?
124 }
125
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200126 template MgcpResponse tr_CRCX_ACK_osmux := {
127 line := {
128 code := "200",
129 trans_id := ?,
130 string := "OK"
131 },
132 params:= { { "I", ? }, {"X-OSMUX", ?}, *},
133 sdp := ?
134 }
135
Harald Welte90029572017-11-24 23:39:50 +0100136 template MgcpResponse ts_CRCX_ACK(MgcpTransId trans_id, MgcpConnectionId conn_id, template SDP_Message sdp := omit) := {
137 line := {
138 code := "200",
139 trans_id := trans_id,
140 string := "OK"
141 },
142 params:= { ts_MgcpParConnectionId(conn_id) },
143 sdp := sdp
144 }
145
Harald Welte4029e8c2017-11-23 22:00:42 +0100146 template MgcpCommand ts_MDCX(MgcpTransId trans_id, charstring ep, MgcpConnectionMode mode, MgcpCallId call_id, MgcpConnectionId conn_id, template SDP_Message sdp := omit) := {
147 line := t_MgcpCmdLine("MDCX", trans_id, ep),
148 params := {
149 t_MgcpParConnMode(mode),
150 ts_MgcpParCallId(call_id),
151 ts_MgcpParConnectionId(conn_id),
152 //t_MgcpParReqId(omit),
Daniel Willmannfbef7142017-11-30 13:16:14 +0100153 t_MgcpParLocConnOpt("p:20, a:AMR")
Harald Welte4029e8c2017-11-23 22:00:42 +0100154 },
155 sdp := sdp
156 }
157
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200158 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) := {
159 line := t_MgcpCmdLine("MDCX", trans_id, ep),
160 params := {
161 t_MgcpParConnMode(mode),
162 ts_MgcpParCallId(call_id),
163 ts_MgcpParConnectionId(conn_id),
164 //t_MgcpParReqId(omit),
165 t_MgcpParLocConnOpt("p:20, a:AMR"),
166 ts_MgcpParOsmuxCID(osmux_cid)
167 },
168 sdp := sdp
169 }
170
Harald Welte90029572017-11-24 23:39:50 +0100171 template MgcpCommand tr_MDCX := {
172 line := t_MgcpCmdLine("MDCX", ?, ?),
173 params := *,
174 sdp := *
175 }
176
Harald Weltebb7523b2018-03-29 08:52:01 +0200177 template MgcpResponse tr_MDCX_ACK := {
178 line := {
179 code := "200",
180 trans_id := ?,
181 string := "OK"
182 },
183 params := *,
184 sdp := ?
185 }
186
Harald Welte90029572017-11-24 23:39:50 +0100187 template MgcpResponse ts_MDCX_ACK(MgcpTransId trans_id, MgcpConnectionId conn_id, template SDP_Message sdp := omit) := ts_CRCX_ACK(trans_id, conn_id, sdp);
188
Harald Welte4029e8c2017-11-23 22:00:42 +0100189 /* have a function that generates a template, rather than a template in order to handle
190 * optional parameters */
191 function ts_DLCX(MgcpTransId trans_id, charstring ep, template MgcpCallId call_id := omit,
192 template MgcpConnectionId conn_id := omit) return template MgcpCommand {
193 var template MgcpCommand cmd;
194 cmd.line := t_MgcpCmdLine("DLCX", trans_id, ep);
195 cmd.params := {};
196 cmd.sdp := omit;
197 if (isvalue(call_id)) {
198 f_mgcp_par_append(cmd.params, ts_MgcpParCallId(valueof(call_id)));
199 if (isvalue(conn_id)) {
200 f_mgcp_par_append(cmd.params, ts_MgcpParConnectionId(valueof(conn_id)));
201 }
202 }
203 return cmd;
204 }
205
Harald Welteb71901a2018-01-26 19:16:05 +0100206 template MgcpCommand tr_DLCX(template MgcpEndpoint ep := ?) := {
207 line := t_MgcpCmdLine("DLCX", ?, ep),
Harald Welte90029572017-11-24 23:39:50 +0100208 params := *,
209 sdp := *
210 }
211
Harald Weltec82eef42017-11-24 20:40:12 +0100212 template MgcpResponse tr_DLCX_ACK := {
213 line := {
Daniel Willmann961e5c92017-11-30 16:37:40 +0100214 code := ("200", "250"),
Harald Weltec82eef42017-11-24 20:40:12 +0100215 trans_id := ?,
216 string := "OK"
217 },
218 params:= *,
219 sdp := *
220 }
221
Harald Welteb71901a2018-01-26 19:16:05 +0100222 template MgcpResponse ts_DLCX_ACK2(MgcpTransId trans_id) := {
223 line := {
224 code := "250",
225 trans_id := trans_id,
226 string := "OK"
227 },
228 params:= { /* list of ConnectionIDs */ },
229 sdp := omit
230 }
231
232
233
Harald Welte90029572017-11-24 23:39:50 +0100234 template MgcpResponse ts_DLCX_ACK(MgcpTransId trans_id, MgcpConnectionId conn_id, template SDP_Message sdp := omit) := ts_CRCX_ACK(trans_id, conn_id, sdp);
235
Harald Weltee98bb2e2017-11-29 12:09:48 +0100236 template MgcpCommand tr_RSIP := {
237 line := t_MgcpCmdLine("RSIP", ?, ?),
238 params := *,
239 sdp := *
240 }
241
Harald Welte4029e8c2017-11-23 22:00:42 +0100242 /* SDP Templates */
243 template SDP_Origin ts_SDP_origin(charstring addr, charstring session_id,
244 charstring session_version := "1",
245 charstring addr_type := "IP4",
246 charstring user_name := "-") := {
247 user_name := user_name,
248 session_id := session_id,
249 session_version := session_version,
250 net_type := "IN",
251 addr_type := addr_type,
252 addr := addr
253 }
254
255 template SDP_connection ts_SDP_connection_IP(charstring addr, charstring addr_type := "IP4",
256 template integer ttl := omit,
257 template integer num_of_addr := omit) :={
258 net_type := "IN",
259 addr_type := addr_type,
260 conn_addr := {
261 addr := addr,
262 ttl := ttl,
263 num_of_addr := num_of_addr
264 }
265 }
266
267 template SDP_time ts_SDP_time(charstring beg, charstring end) := {
268 time_field := {
269 start_time := beg,
270 stop_time := end
271 },
272 time_repeat := omit
273 }
274
275 template SDP_media_desc ts_SDP_media_desc(integer port_number, SDP_fmt_list fmts,
276 SDP_attribute_list attributes) := {
277 media_field := {
278 media := "audio",
279 ports := {
280 port_number := port_number,
281 num_of_ports := omit
282 },
283 transport := "RTP/AVP",
284 fmts := fmts
285 },
286 information := omit,
287 connections := omit,
288 bandwidth := omit,
289 key := omit,
290 attributes := attributes
291 }
292
293 /* master template for generating SDP based in template arguments */
294 template SDP_Message ts_SDP(charstring local_addr, charstring remote_addr,
295 charstring session_id, charstring session_version,
296 integer rtp_port, SDP_fmt_list fmts,
297 SDP_attribute_list attributes) := {
298 protocol_version := 0,
299 origin := ts_SDP_origin(local_addr, session_id, session_version),
300 session_name := "-",
301 information := omit,
302 uri := omit,
303 emails := omit,
304 phone_numbers := omit,
305 connection := ts_SDP_connection_IP(remote_addr),
306 bandwidth := omit,
307 times := { ts_SDP_time("0","0") },
308 timezone_adjustments := omit,
309 key := omit,
310 attributes := omit,
311 media_list := { ts_SDP_media_desc(rtp_port, fmts, attributes) }
312 }
313
314 template SDP_attribute ts_SDP_rtpmap(integer fmt, charstring val) := {
315 rtpmap := {
316 attr_value := int2str(fmt) & " " & val
317 }
318 }
319 template SDP_attribute ts_SDP_ptime(integer p) := {
320 ptime := {
321 attr_value := int2str(p)
322 }
323 }
Philipp Maierc8c0b402019-03-07 10:48:45 +0100324 template SDP_attribute ts_SDP_fmtp(integer fmt, charstring val) := {
325 fmtp := {
326 attr_value := int2str(fmt) & " " & val
327 }
328 }
Harald Welte4029e8c2017-11-23 22:00:42 +0100329
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200330 /* -1 is wildcard, positive is translated as string */
331 function f_mgcp_osmux_cid_encode(MgcpOsmuxCID osmux_cid) return charstring {
332 if (osmux_cid == -1) {
333 return "*";
334 }
335 return int2str(osmux_cid);
336 }
337
338 function f_mgcp_osmux_cid_decode(charstring osmux_cid) return MgcpOsmuxCID {
339 if (osmux_cid == "*") {
340 return -1;
341 }
342 return str2int(osmux_cid);
343 }
344
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100345 function f_mgcp_contains_par(MgcpMessage msg, MgcpInfoCode code) return boolean {
346 var MgcpParameterList pars;
347 if (ischosen(msg.command)) {
348 pars := msg.command.params;
349 } else {
350 pars := msg.response.params;
351 }
352 for (var integer i := 0; i < lengthof(pars); i := i + 1) {
353 var MgcpParameter par := pars[i];
354 if (par.code == code) {
355 return true;
356 }
357 }
358 return false;
359 }
360
Harald Welteb71901a2018-01-26 19:16:05 +0100361 function f_mgcp_extract_par(MgcpMessage msg, MgcpInfoCode code) return charstring {
362 var MgcpParameterList pars;
363 if (ischosen(msg.command)) {
364 pars := msg.command.params;
365 } else {
366 pars := msg.response.params;
367 }
368 for (var integer i := 0; i < lengthof(pars); i := i + 1) {
369 var MgcpParameter par := pars[i];
370 if (par.code == code) {
371 return par.val;
Harald Welte4c11d562017-11-24 23:39:00 +0100372 }
373 }
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200374 setverdict(fail, "Could not extract parameters for code ", code);
Harald Welteb71901a2018-01-26 19:16:05 +0100375 return "";
376 }
377
Harald Welte1fe74812018-01-29 21:57:26 +0100378 function f_MgcpResp_extract_par(MgcpResponse resp, MgcpInfoCode code) return charstring {
Harald Welteb71901a2018-01-26 19:16:05 +0100379 var MgcpMessage msg := {
380 response := resp
381 }
Harald Welte1fe74812018-01-29 21:57:26 +0100382 return f_mgcp_extract_par(msg, code);
Harald Welteb71901a2018-01-26 19:16:05 +0100383 }
384
Harald Welte1fe74812018-01-29 21:57:26 +0100385 function f_MgcpCmd_extract_par(MgcpCommand cmd, MgcpInfoCode code) return charstring {
Harald Welteb71901a2018-01-26 19:16:05 +0100386 var MgcpMessage msg := {
387 command := cmd
388 }
Harald Welte1fe74812018-01-29 21:57:26 +0100389 return f_mgcp_extract_par(msg, code);
Harald Welte4c11d562017-11-24 23:39:00 +0100390 }
391
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100392 function f_MgcpCmd_contains_par(MgcpCommand cmd, MgcpInfoCode code) return boolean {
393 var MgcpMessage msg := {
394 command := cmd
395 }
396 return f_mgcp_contains_par(msg, code);
397 }
398
Harald Welte1fe74812018-01-29 21:57:26 +0100399 function f_MgcpResp_extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
400 return str2hex(f_MgcpResp_extract_par(resp, "I"));
401 }
402
403 function f_MgcpCmd_extract_call_id(MgcpCommand cmd) return MgcpCallId {
404 return str2hex(f_MgcpCmd_extract_par(cmd, "C"));
405 }
406
407 function f_MgcpCmd_extract_conn_id(MgcpCommand cmd) return MgcpConnectionId {
408 return str2hex(f_MgcpCmd_extract_par(cmd, "I"));
409 }
410
Pau Espin Pedrolb2c6b382019-05-14 13:40:49 +0200411 function f_MgcpCmd_extract_osmux_cid(MgcpCommand cmd) return MgcpOsmuxCID {
412 return f_mgcp_osmux_cid_decode(f_MgcpCmd_extract_par(cmd, "X-OSMUX"));
413 }
414
Harald Welte1fe74812018-01-29 21:57:26 +0100415
Harald Welte4c11d562017-11-24 23:39:00 +0100416 function f_mgcp_alloc_tid() return MgcpTransId {
417 return int2str(float2int(rnd()*2147483647.0));
418 }
419
420 function f_mgcp_alloc_call_id() return MgcpCallId {
421 return int2hex(float2int(rnd()*2147483647.0), 8);
422 }
423
424 function f_mgcp_alloc_conn_id() return MgcpConnectionId {
425 return int2hex(float2int(rnd()*2147483647.0), 8);
426 }
427
Harald Weltebb5a1212018-01-26 10:34:44 +0100428 /* those verbs that related to a connection (and hence have ConnectionId) */
429 template MgcpVerb tr_MgcpVerb_ConnectionOriented := ("CRCX", "MDCX", "DLCX", "AUCX");
430 /* entire command template matching only connection oriented verbs */
431 template MgcpCommand tr_MgcpCommand_CO := {
432 line := {
433 verb := tr_MgcpVerb_ConnectionOriented,
434 trans_id := ?,
435 ep := ?,
436 ver := ?
437 },
438 params := *,
439 sdp := *
440 }
441
Harald Welte363cb0a2018-01-30 19:35:53 +0100442 function f_mgcp_find_param(MgcpMessage msg, MgcpInfoCode code, out charstring ret)
443 return boolean {
444 var MgcpParameterList pars;
445 if (ischosen(msg.command)) {
446 pars := msg.command.params;
447 } else {
448 pars := msg.response.params;
449 }
450 for (var integer i := 0; i < sizeof(pars); i := i+1) {
451 if (pars[i].code == code) {
452 ret := pars[i].val;
453 return true;
454 }
455 }
456 return false;
457 }
458
459 /* template to determine if a MGCP endpoint is a wildcard endpoint */
460 template charstring t_MGCP_EP_wildcard := (pattern "\*@*", pattern "rtpbridge/\*@*");
461
Harald Welteb71901a2018-01-26 19:16:05 +0100462
Harald Welte4029e8c2017-11-23 22:00:42 +0100463}