blob: 4f3cbb79ecd9f5092f2c74bfba48ae6687ab5c1d [file] [log] [blame]
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +02001/* Component implementing a IMS server towards Asterisk's IMS UE
2 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
3 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11module IMS_ConnectionHandler {
12
Pau Espin Pedrol717379f2024-05-17 18:36:51 +020013import from TCCEncoding_Functions all;
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020014import from TCCOpenSecurity_Functions all;
15import from General_Types all;
16import from Osmocom_Types all;
17import from Native_Functions all;
18import from Misc_Helpers all;
19
Pau Espin Pedrola674d612024-05-14 19:56:33 +020020/* the PIPE asp port allows us to interact with ip xfrm via stdin/stdout */
21import from PIPEasp_PortType all;
22import from PIPEasp_Types all;
23import from PIPEasp_Templates all;
24
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020025import from SDP_Types all;
26import from SDP_Templates all;
27
28import from SIP_Emulation all;
29import from SIPmsg_Types all;
30import from SIP_Templates all;
31
Pau Espin Pedrola674d612024-05-14 19:56:33 +020032
33modulepar {
34 charstring mp_ipsec_setup_script_path := "./IMS_ipsec_setup.sh";
35}
36
Pau Espin Pedrolcb0dbf92024-06-06 20:40:30 +020037const integer c_def_expires := 600000; /* 3GPP TS 24.229 5.1.1.2.1 e) */
Pau Espin Pedrola2424b22024-06-10 20:23:13 +020038const charstring c_sip_server_name := "osmo-ttcn3-hacks/0.23";
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +020039
40
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020041type port IMSCoord_PT message
42{
43 inout charstring;
44} with { extension "internal" };
45
Pau Espin Pedrol5acf7c62024-06-06 19:23:08 +020046const charstring IMS_COORD_CMD_REGISTERED := "IMS_COORD_CMD_REGISTERED";
47const charstring IMS_COORD_CMD_START := "IMS_COORD_CMD_START";
48const charstring IMS_COORD_CMD_CALL_ESTABLISHED := "IMS_COORD_CMD_CALL_ESTABLISHED";
49const charstring IMS_COORD_CMD_HANGUP := "IMS_COORD_CMD_HANGUP";
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020050
51type component IMS_ConnHdlr extends SIP_ConnHdlr {
52 var charstring g_name;
53 var IMS_ConnHdlrPars g_pars;
54 timer g_Tguard;
55 var PDU_SIP_Request g_rx_sip_req;
56 var PDU_SIP_Response g_rx_sip_resp;
57
58 port IMSCoord_PT COORD;
Pau Espin Pedrola674d612024-05-14 19:56:33 +020059 port PIPEasp_PT PIPE;
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020060}
61type record of IMS_ConnHdlr IMS_ConnHdlrList;
62
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +020063type record IMS_AuthVector {
64 OCT16 rand,
65 OCT16 autn,
66 OCT8 res,
67 OCT16 ck,
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +020068 OCT16 ik,
69 OCT14 auts
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +020070}
71
Pau Espin Pedrol901cede2024-05-30 13:03:42 +020072type record IMS_ConnHdlrSubscrPars {
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +020073 charstring remote_sip_host optional,
74 uint16_t remote_sip_port optional,
Pau Espin Pedrol901cede2024-05-30 13:03:42 +020075 charstring imsi,
Pau Espin Pedrola017f442024-06-20 16:04:51 +020076 charstring imei,
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020077 charstring display_name,
78 charstring password,
Pau Espin Pedrol4e6672c2024-05-22 17:03:53 +020079 charstring msisdn,
Pau Espin Pedrola017f442024-06-20 16:04:51 +020080 boolean support_video,
81 boolean support_smsip,
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +020082 /* Expected User-Location-Info in P-Access-Network-Info */
83 charstring uli_str,
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +020084 IMS_AuthVector auth,
Pau Espin Pedrola674d612024-05-14 19:56:33 +020085 charstring ipsec_auth_key,
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +020086 integer ipsec_local_spi_c,
87 integer ipsec_local_spi_s,
88 integer ipsec_remote_spi_c optional,
89 integer ipsec_remote_spi_s optional,
Pau Espin Pedrola674d612024-05-14 19:56:33 +020090 uint16_t ipsec_remote_port_c optional,
91 uint16_t ipsec_remote_port_s optional,
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020092 SipAddr registrar_sip_record,
93 CallidString registrar_sip_call_id,
94 integer registrar_sip_seq_nr,
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020095 SipUrl local_sip_url_ext,
96 SipAddr local_sip_record,
Pau Espin Pedrol5acf7c62024-06-06 19:23:08 +020097 Contact registered_contact optional,
Pau Espin Pedrol586eec52024-06-04 19:07:33 +020098 P_Associated_Uri p_associated_uri,
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020099 IMS_CallPars cp optional
100}
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200101type record of IMS_ConnHdlrSubscrPars IMS_ConnHdlrSubscrParsList;
102
103
104type record IMS_ConnHdlrPars {
105 float t_guard,
106 charstring realm,
107 charstring local_sip_host,
108 uint16_t local_sip_port,
109 SipUrl registrar_sip_req_uri,
110 Via local_via,
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200111 Server server_name,
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200112 IMS_ConnHdlrSubscrPars subscr optional
113}
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +0200114type record of IMS_ConnHdlrPars IMS_ConnHdlrParsList;
115
116type record IMS_CallParsMT {
117 /* Whether to wait for COORD.receive(COORD_CMD_PICKUP) before accepting the call. */
118 boolean wait_coord_cmd_pickup,
119 /* Whether to expect CANCEL instead of ACK as answer to our OK */
120 boolean exp_cancel
121}
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200122template (value) IMS_CallParsMT t_IMS_CallParsMT := {
123 wait_coord_cmd_pickup := false,
124 exp_cancel := false
125}
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +0200126
127type record IMS_CallPars {
128 SipAddr calling optional,
129 SipAddr called optional,
130
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200131 From from_addr optional,
132 To to_addr optional,
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +0200133
134 CallidString sip_call_id,
135 integer sip_seq_nr,
136 charstring sip_body optional,
137
138 charstring local_rtp_addr,
139 uint16_t local_rtp_port,
140
141 SDP_Message peer_sdp optional,
142 IMS_CallParsMT mt
143}
144
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200145template (value) IMS_CallPars t_IMS_CallPars(charstring local_rtp_addr,
146 uint16_t local_rtp_port := 0,
147 template (omit) SipAddr calling := omit,
148 template (omit) SipAddr called := omit) := {
149 calling := calling,
150 called := called,
151 from_addr := omit,
152 to_addr := omit,
153 sip_call_id := hex2str(f_rnd_hexstring(15)),
154 sip_seq_nr := f_sip_rand_seq_nr(),
155 sip_body := omit,
156 local_rtp_addr := local_rtp_addr,
157 local_rtp_port := local_rtp_port,
158 peer_sdp := omit,
159 mt := t_IMS_CallParsMT
160}
161
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200162template (value) IMS_ConnHdlrSubscrPars t_IMS_SubscrPars(charstring local_sip_host,
163 uint16_t local_sip_port,
Pau Espin Pedrolf46132e2024-06-04 17:11:59 +0200164 charstring domain,
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200165 charstring imsi,
Pau Espin Pedrola017f442024-06-20 16:04:51 +0200166 charstring imei,
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200167 charstring msisdn := "90828",
168 charstring display_name := "Anonymous",
169 charstring password := "secret",
170 template (omit) IMS_CallPars cp := omit) := {
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200171 remote_sip_host := omit,
172 remote_sip_port := omit,
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200173 imsi := imsi,
Pau Espin Pedrola017f442024-06-20 16:04:51 +0200174 imei := imei,
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200175 display_name := f_sip_str_quote(display_name),
176 password := password,
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200177 msisdn := msisdn,
Pau Espin Pedrola017f442024-06-20 16:04:51 +0200178 support_video := false,
179 support_smsip := false,
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200180 uli_str := "2380100010000101",
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +0200181 auth := {
182 /* The Nonce field is the Base64 encoded version of the RAND value and concatenated with the AUTN: */
183 rand := 'd5d5de2bce418d7865ed7fa6956618a2'O,
184 autn := 'd42e61db5f15800067393a5b7691a227'O,
185 res := '6f2556bbe4366ab1'O,
186 ck := '0b389d08c833991734936bec55cac800'O,
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200187 ik := '17141862125bd30c81c4224391a0909a'O,
188 /* NOTE: AUTS value randomly crafted. It's fine since it's just forwarded
189 * AMI -> asterisk -> IMS and we blindly match and accept it. */
190 auts := 'd42e61db5f15800067393a5b7691'O
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +0200191 },
192 ipsec_auth_key := "0x17141862125bd30c81c4224391a0909a00000000",
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200193 ipsec_local_spi_c := 4142,
194 ipsec_local_spi_s := 4143,
195 ipsec_remote_spi_c := omit,
196 ipsec_remote_spi_s := omit,
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200197 ipsec_remote_port_c := omit,
198 ipsec_remote_port_s := omit,
Pau Espin Pedrolf46132e2024-06-04 17:11:59 +0200199 registrar_sip_record := ts_SipAddr(ts_HostPort(domain),
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200200 ts_UserInfo(imsi),
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200201 f_sip_str_quote(display_name)),
Pau Espin Pedrolf46132e2024-06-04 17:11:59 +0200202 registrar_sip_call_id := hex2str(f_rnd_hexstring(15)) & "@" & domain,
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200203 registrar_sip_seq_nr := f_sip_rand_seq_nr(),
Pau Espin Pedrolf46132e2024-06-04 17:11:59 +0200204 local_sip_url_ext := ts_SipUrl(ts_HostPort(domain, local_sip_port),
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200205 ts_UserInfo(imsi)),
Pau Espin Pedrolf46132e2024-06-04 17:11:59 +0200206 local_sip_record := ts_SipAddr(ts_HostPort(domain),
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200207 ts_UserInfo(imsi)),
Pau Espin Pedrol5acf7c62024-06-06 19:23:08 +0200208 registered_contact := omit,
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200209 p_associated_uri := ts_P_Associated_Uri({}),
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200210 cp := cp
211}
212
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200213template (value) IMS_ConnHdlrPars t_IMS_Pars(charstring local_sip_host,
214 uint16_t local_sip_port,
Pau Espin Pedrolf46132e2024-06-04 17:11:59 +0200215 charstring domain,
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200216 charstring imsi,
Pau Espin Pedrola017f442024-06-20 16:04:51 +0200217 charstring imei,
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200218 template (omit) IMS_CallPars cp := omit) := {
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200219 t_guard := 60.0,
Pau Espin Pedrolf46132e2024-06-04 17:11:59 +0200220 realm := domain,
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200221 local_sip_host := local_sip_host,
222 local_sip_port := local_sip_port,
Pau Espin Pedrolf46132e2024-06-04 17:11:59 +0200223 registrar_sip_req_uri := valueof(ts_SipUrlHost(domain)),
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200224 local_via := ts_Via_from(ts_HostPort(local_sip_host, local_sip_port)),
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200225 server_name := valueof(ts_Server({c_sip_server_name})),
Pau Espin Pedrola017f442024-06-20 16:04:51 +0200226 subscr := t_IMS_SubscrPars(local_sip_host, local_sip_port,
227 domain := domain, imsi := imsi,
228 imei := imei, cp := cp)
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200229}
230
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200231private altstep as_Tguard() runs on IMS_ConnHdlr {
232 [] g_Tguard.timeout {
233 setverdict(fail, "Tguard timeout");
234 mtc.stop;
235 }
236}
237
238type function ims_void_fn(charstring id) runs on IMS_ConnHdlr;
239function f_ims_handler_init(ims_void_fn fn, charstring id, IMS_ConnHdlrPars pars)
240runs on IMS_ConnHdlr {
241 g_name := id;
242 g_pars := pars;
243 g_Tguard.start(pars.t_guard);
244 activate(as_Tguard());
245
246 /* call the user-supied test case function */
247 fn.apply(id);
248}
249
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200250altstep as_SIP_fail_req(charstring exp_msg_str := "") runs on IMS_ConnHdlr
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200251{
252 var PDU_SIP_Request sip_req;
253 [] SIP.receive(PDU_SIP_Request:?) -> value sip_req {
254 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
255 log2str(g_name & ": Received unexpected SIP Req message := ", sip_req, "\nvs exp := ", exp_msg_str));
256 }
257}
258
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200259altstep as_SIP_fail_resp(charstring exp_msg_str := "") runs on IMS_ConnHdlr
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200260{
261 var PDU_SIP_Response sip_resp;
262 [] SIP.receive(PDU_SIP_Response:?) -> value sip_resp {
263 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
264 log2str(g_name & ": Received unexpected SIP Resp message := ", sip_resp, "\nvs exp := ", exp_msg_str));
265 }
266}
267
Pau Espin Pedrol5acf7c62024-06-06 19:23:08 +0200268altstep as_SIP_expect_resp(template (present) PDU_SIP_Response sip_expect, boolean fail_others := true) runs on IMS_ConnHdlr
269{
270 var charstring sip_expect_str := log2str(sip_expect);
271 [] SIP.receive(sip_expect) -> value g_rx_sip_resp;
272 [fail_others] as_SIP_fail_resp(sip_expect_str);
273 [fail_others] as_SIP_fail_req(sip_expect_str);
274}
275
276altstep as_SIP_ignore_resp(template PDU_SIP_Response sip_expect := ?) runs on IMS_ConnHdlr
277{
278 [] SIP.receive(sip_expect) -> value g_rx_sip_resp {
279 log("Ignoring ", g_rx_sip_resp);
280 repeat;
281 }
282}
283
Pau Espin Pedrol717379f2024-05-17 18:36:51 +0200284private function f_nonce_from_rand_autn(octetstring rand, octetstring autn) return charstring {
285 var octetstring concat := rand & autn;
286 var charstring nonce := enc_MIME_Base64(concat);
287 log("rand=", rand, " & autn=",autn, " => nonce=", nonce);
288 return nonce;
289}
290
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200291/* HTTP Digest Authentication Using AKA (AKAv1-MD5): RFC 3310 */
292function f_tr_Authorization_AKAv1MD5(WwwAuthenticate www_authenticate,
293 charstring username,
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +0200294 charstring uri)
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200295return template (present) Authorization {
296 var CommaParam_List digestCln;
297 var template (present) Authorization authorization;
298 var template (present) Credentials cred;
299 var template (omit) GenericParam rx_param;
300
301 digestCln := www_authenticate.challenge[0].digestCln;
302
303 var charstring algorithm := f_sip_param_get_value_present_or_fail(digestCln, "algorithm");
304 var charstring realm := f_sip_param_get_value_present_or_fail(digestCln, "realm");
305 var charstring nonce := f_sip_param_get_value_present_or_fail(digestCln, "nonce");
306
307 var template (present) CommaParam_List digestResponse := superset(
308 tr_Param("username", f_sip_str_quote(username)),
309 tr_Param("realm", f_sip_str_quote(realm)),
310 tr_Param("nonce", f_sip_str_quote(nonce)),
311 tr_Param("uri", f_sip_str_quote(uri)),
312 tr_Param("response", ?),
313 tr_Param("algorithm", algorithm),
314 tr_Param("qop", "auth"),
315 tr_Param("cnonce", ?),
316 tr_Param("nc", ?)
317 );
318 cred := tr_Credentials_DigestResponse(digestResponse);
319 authorization := tr_Authorization(cred);
320 return authorization;
321}
322
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +0200323private function f_ims_validate_Authorization_AKAv1MD5_Response(Authorization authorization, charstring method)
324runs on IMS_ConnHdlr {
325 f_sip_digest_validate_Authorization_AKAv1MD5(authorization, method, g_pars.subscr.auth.res);
326}
327
328
Pau Espin Pedrola017f442024-06-20 16:04:51 +0200329private function f_ims_validate_register_contact(Contact rx_contact) runs on IMS_ConnHdlr
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200330{
Pau Espin Pedrola017f442024-06-20 16:04:51 +0200331 /* IMS contact shows up like this:
332 * Contact: <sip:8adf9f3d-9342-4060-aa4f-a909f37fd6f6@192.168.101.2:5060>;+g.3gpp.accesstype="cellular2";video;audio;+g.3gpp.smsip;+g.3gpp.nw-init-ussi;+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel";+sip.instance="<urn:gsma:imei:35589811-338445-0>"
333 */
334 if (ischosen(rx_contact.contactBody.wildcard)) {
335 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
336 log2str(g_name & ": Received unexpected Contact wildcard := ", rx_contact));
337 }
338 var ContactAddress_List caddrs := rx_contact.contactBody.contactAddresses;
339 if (lengthof(caddrs) == 0) {
340 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
341 log2str(g_name & ": Received unexpected empty Contact Address list"));
342 }
343 for (var integer i := 0; i < lengthof(caddrs); i := i + 1) {
344 var ContactAddress caddr := caddrs[i];
345 var template (omit) GenericParam param_tpl;
346 var boolean rx_supported;
347 if (not ispresent(caddr.contactParams)) {
348 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
349 log2str(g_name & ": Received unexpected empty Contact attributes (omit)"));
350 }
351 f_sip_param_match_value_or_fail(caddr.contactParams, "+g.3gpp.icsi-ref", "\"urn\\%3Aurn-7\\%3A3gpp-service.ims.icsi.mmtel\"");
352 f_sip_param_match_value_or_fail(caddr.contactParams, "+sip.instance", "\"<urn:gsma:imei:" & g_pars.subscr.imei & ">\"");
353 f_sip_param_match_value_or_fail(caddr.contactParams, "audio", omit);
354
355 /* Check video support */
356 param_tpl := f_sip_param_find(caddr.contactParams, "video");
357 rx_supported := not istemplatekind(param_tpl, "omit");
358 if (rx_supported) {
359 /* This attribute never has a value: */
360 if (not istemplatekind(param_tpl.paramValue, "omit")) {
361 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
362 log2str(g_name & ": Received Contact with 'video' attribute containing a value := ", rx_contact));
363 }
364 if (not g_pars.subscr.support_video) {
365 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
366 log2str(g_name & ": Received Contact with unexpected 'video' attribute := ", rx_contact));
367 }
368 } else if (g_pars.subscr.support_video) {
369 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
370 log2str(g_name & ": Received Contact with missing 'video' attribute := ", rx_contact));
371 }
372
373 /* Check smsip support */
374 param_tpl := f_sip_param_find(caddr.contactParams, "+g.3gpp.smsip");
375 rx_supported := not istemplatekind(param_tpl, "omit");
376 if (rx_supported) {
377 /* This attribute never has a value: */
378 if (not istemplatekind(param_tpl.paramValue, "omit")) {
379 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
380 log2str(g_name & ": Received Contact with '+g.3gpp.smsip' attribute containing a value := ", rx_contact));
381 }
382 if (not g_pars.subscr.support_smsip) {
383 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
384 log2str(g_name & ": Received Contact with unexpected '+g.3gpp.smsip' attribute := ", rx_contact));
385 }
386 } else if (g_pars.subscr.support_smsip) {
387 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
388 log2str(g_name & ": Received Contact with missing '+g.3gpp.smsip' attribute := ", rx_contact));
389 }
390 }
391
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200392}
393
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200394/* Validate P-Access-Network-Info: RFC7315 6.4 */
395private function f_ims_validate_register_P_Access_Network_info(PDU_SIP_Request req,
396 boolean exp_present := true) runs on IMS_ConnHdlr
397
398{
399 if (not exp_present) {
400 if (ispresent(g_rx_sip_req.msgHeader.p_access_network_info)) {
401 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
402 log2str(g_name & ": Received unexpected [rfc7315 6.4] P-Access-Info := ",
403 g_rx_sip_req.msgHeader.p_access_network_info));
404 }
405 return;
406 }
407
408 /* exp_present: */
409 var template (present) P_Access_Network_Info expl_tmpl :=
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200410 tr_P_Access_Network_Info({ tr_Access_net_spec_EUTRAN(g_pars.subscr.uli_str) });
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200411
412 if (not ispresent(g_rx_sip_req.msgHeader.p_access_network_info)) {
413 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
414 log2str(g_name & ": Received no P-Access-Info vs exp := ",
415 expl_tmpl));
416 }
417 if (not match(g_rx_sip_req.msgHeader.p_access_network_info, expl_tmpl)) {
418 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
419 log2str(g_name & ": Received unexpected P-Access-Info := ",
420 g_rx_sip_req.msgHeader.p_access_network_info,
421 "\nvs exp := ", expl_tmpl));
422 }
423}
424
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200425private function f_ims_parse_security_client(Security_client security_client) runs on IMS_ConnHdlr
426{
427 var boolean found := false;
428 for (var integer i := 0; i < lengthof(security_client.sec_mechanism_list); i := i + 1) {
429 var Security_mechanism sec_mec := security_client.sec_mechanism_list[i];
430 if (sec_mec.mechanism_name != "ipsec-3gpp") {
431 log("Skipping Security Mechansim: ", sec_mec.mechanism_name);
432 continue;
433 }
434 var SemicolonParam_List sec_pars := sec_mec.mechanism_params;
435 var charstring par_val;
436 par_val := f_sip_param_get_value_present_or_fail(sec_pars, "alg");
437 if (par_val != "hmac-sha-1-96") {
438 log("Skipping Security Mechansim Algo: ", par_val);
439 continue;
440 }
441 par_val := f_sip_param_get_value_present_or_fail(sec_pars, "spi-c");
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200442 g_pars.subscr.ipsec_remote_spi_c := str2int(par_val);
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200443 par_val := f_sip_param_get_value_present_or_fail(sec_pars, "spi-s");
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200444 g_pars.subscr.ipsec_remote_spi_s := str2int(par_val);
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200445 par_val := f_sip_param_get_value_present_or_fail(sec_pars, "port-c");
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200446 g_pars.subscr.ipsec_remote_port_c := str2int(par_val);
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200447 par_val := f_sip_param_get_value_present_or_fail(sec_pars, "port-s");
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200448 g_pars.subscr.ipsec_remote_port_s := str2int(par_val);
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200449 found := true;
450 break;
451 }
452
453 if (not found) {
454 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
455 log2str(g_name & "alg=hmac-sha-1-96 not found: ", security_client));
456 }
457
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200458 log("ipsec: remote_spi_c=", g_pars.subscr.ipsec_remote_spi_c, " remote_spi_s=", g_pars.subscr.ipsec_remote_spi_s,
459 "local_spi_c=", g_pars.subscr.ipsec_local_spi_c, " local_spi_s=", g_pars.subscr.ipsec_local_spi_s);
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200460}
461
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200462private function f_ims_parse_register_contact(Contact contact) runs on IMS_ConnHdlr
463{
464 var HostPort hp := valueof(contact.contactBody.contactAddresses[0].addressField.nameAddr.addrSpec.hostPort);
465
466 g_pars.subscr.remote_sip_host := hp.host;
467 if (ispresent(hp.portField)) {
468 g_pars.subscr.remote_sip_port := hp.portField;
469 } else {
470 g_pars.subscr.remote_sip_port := 5060;
471 }
472}
473
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200474private function f_IMS_exec_sync(charstring cmdline, template (present) integer rc := 0)
475 runs on IMS_ConnHdlr return ASP_PResult {
476 var ASP_PResult res;
477
478 map(self:PIPE, system:PIPE);
479 res := f_PIPEasp_exec_sync_PResult(PIPE, cmdline, tr_PResult(?, ?, rc));
480 unmap(self:PIPE, system:PIPE);
481
482 return res;
483}
484
485private function f_ims_setup_ipsec() runs on IMS_ConnHdlr
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200486{
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200487 var ASP_PResult res;
488
489 var charstring cmd := mp_ipsec_setup_script_path & " " &
490 g_pars.local_sip_host & " " &
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200491 int2str(g_pars.local_sip_port) & " " & int2str(g_pars.subscr.ipsec_local_spi_c) & " " &
492 int2str(g_pars.local_sip_port) & " " & int2str(g_pars.subscr.ipsec_local_spi_s) & " " &
493 g_pars.subscr.remote_sip_host & " " &
494 int2str(g_pars.subscr.ipsec_remote_port_c) & " " & int2str(g_pars.subscr.ipsec_remote_spi_c) & " " &
495 int2str(g_pars.subscr.ipsec_remote_port_s) & " " & int2str(g_pars.subscr.ipsec_remote_spi_s) & " " &
496 g_pars.subscr.ipsec_auth_key;
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200497
498 res := f_IMS_exec_sync(cmd);
499
500 /* Debug applied rules: */
501 /*
502 res := f_IMS_exec_sync("ip xfrm state");
503 log("ip-xfrm-state Result-Stdout: " & res.stdout);
504
505 res := f_IMS_exec_sync("ip xfrm policy");
506 log("ip-xfrm-policy Result-Stdout: " & res.stdout);
507 */
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200508}
509
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200510private function f_tr_Via_response(Via via_req) return template (present) Via {
511 template (present) SemicolonParam_List via_resp_params := ?;
512
513 /*via_resp_params := {
514 { id := "rport", paramValue := int2str(g_pars.subscr.remote_sip_port.subscr.remote_sip_port) },
515 { id := "received", paramValue := g_pars.subscr.remote_sip_host }
516 }; */
517 return tr_Via_from(via_req.viaBody[0].sentBy,
518 via_req.viaBody[0].sentProtocol.transport,
519 via_resp_params);
520}
521
522private function f_tr_From(template (value) SipAddr from_req) return template (present) SipAddr {
523 return tr_SipAddr_from_val(from_req);
524}
525
526private altstep as_SIP_expect_req(template (present) PDU_SIP_Request sip_expect, boolean fail_others := true) runs on IMS_ConnHdlr
527{
528 var charstring sip_expect_str := log2str(sip_expect);
529 [] SIP.receive(sip_expect) -> value g_rx_sip_req;
530 [fail_others] as_SIP_fail_req(sip_expect_str);
531 [fail_others] as_SIP_fail_resp(sip_expect_str);
532}
533
534private function f_gen_sdp() runs on IMS_ConnHdlr return charstring {
535 var charstring sdp :=
536 "v=0\r\n" &
537 "o=0502 2390 1824 IN IP4 " & g_pars.subscr.cp.local_rtp_addr & "\r\n" &
538 "s=Talk\r\n" &
539 "c=IN IP4 " & g_pars.subscr.cp.local_rtp_addr & "\r\n" &
540 "t=0 0\r\n" &
541 "a=rtcp-xr:rcvr-rtt=all:10000 stat-summary=loss,dup,jitt,TTL voip-metrics\r\n" &
542 "a=record:off\r\n" &
543 "m=audio " & int2str(g_pars.subscr.cp.local_rtp_port) & " RTP/AVP 8 96 97 98 0 18 99 100 101\r\n" &
544 "a=rtpmap:8 PCMA/8000\r\n" &
545 "a=rtpmap:96 opus/48000/2\r\n" &
546 "a=fmtp:96 useinbandfec=1\r\n" &
547 "a=rtpmap:97 speex/16000\r\n" &
548 "a=fmtp:97 vbr=on\r\n" &
549 "a=rtpmap:98 speex/8000\r\n" &
550 "a=fmtp:98 vbr=on\r\n" &
551 "a=fmtp:18 annexb=yes\r\n" &
552 "a=rtpmap:99 telephone-event/48000\r\n" &
553 "a=rtpmap:100 telephone-event/16000\r\n" &
554 "a=rtpmap:101 telephone-event/8000\r\n" &
555 "a=rtcp:" & int2str(g_pars.subscr.cp.local_rtp_port + 1) & "\r\n" &
556 "a=rtcp-fb:* trr-int 1000\r\n" &
557 "a=rtcp-fb:* ccm tmmbr\r\n";
558 return sdp;
559}
560
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200561private function f_gen_Security_server() runs on IMS_ConnHdlr return Security_server {
562 var template (value) Security_server security_server;
563 /* Security-Server: ipsec-3gpp;q=0.1;prot=esp;mod=trans;spi-c=4096;spi-s=4097;port-c=5104;port-s=6104;alg=hmac-sha-1-96;ealg=null */
564 var template (value) SemicolonParam_List sec_params := {
565 ts_Param("q", "0.1"),
566 ts_Param("prot", "esp"),
567 ts_Param("mod", "trans"),
568 ts_Param("spi-c", int2str(g_pars.subscr.ipsec_local_spi_c)),
569 ts_Param("spi-s", int2str(g_pars.subscr.ipsec_local_spi_s)),
570 ts_Param("port-c", int2str(g_pars.local_sip_port)),
571 ts_Param("port-s", int2str(g_pars.local_sip_port)),
572 ts_Param("alg", "hmac-sha-1-96"),
573 ts_Param("ealg", "null")
574 };
575 security_server := ts_Security_server({
576 ts_Security_mechanism("ipsec-3gpp", sec_params)
577 });
578 return valueof(security_server);
579}
580
581private function f_gen_WwwAuthenticate() runs on IMS_ConnHdlr return WwwAuthenticate {
582 var template (value) WwwAuthenticate wwwAuthenticate;
583 var template (value) CommaParam_List digestCln;
584 digestCln := {
585 ts_Param("realm", f_sip_str_quote(g_pars.realm)),
586 ts_Param("qop", f_sip_str_quote("auth")),
587 ts_Param("algorithm", "AKAv1-MD5"),
588 ts_Param("nonce", f_sip_str_quote(f_nonce_from_rand_autn(g_pars.subscr.auth.rand,
589 g_pars.subscr.auth.autn)))
590 /* "opaque not needed in IMS "*/
591 };
592 wwwAuthenticate := ts_WwwAuthenticate( { ts_Challenge_digestCln(digestCln) } );
593 return valueof(wwwAuthenticate);
594}
595
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200596type enumerated IMS_register_early_return {
597 IMS_REG_EARLY_RET_BEFORE_None,
598 IMS_REG_EARLY_RET_BEFORE_Initial_100Trying,
599 IMS_REG_EARLY_RET_BEFORE_Initial_401Unauthorized,
600 IMS_REG_EARLY_RET_BEFORE_Resync_401Unauthorized,
601 IMS_REG_EARLY_RET_BEFORE_Protected_100Trying,
602 IMS_REG_EARLY_RET_BEFORE_Protected_200OK
603}
604
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200605/* Peer is issuing 1st register, accept it: */
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200606altstep as_IMS_register(boolean exp_auth_resync := false,
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200607 IMS_register_early_return early_ret := IMS_REG_EARLY_RET_BEFORE_None,
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200608 boolean fail_others := true) runs on IMS_ConnHdlr
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200609{
610 var template (present) PDU_SIP_Request exp_req :=
611 tr_SIP_REGISTER(g_pars.registrar_sip_req_uri,
612 ?,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200613 tr_From(),
614 tr_To(),
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200615 tr_Via_from(?),
Pau Espin Pedrolcb0dbf92024-06-06 20:40:30 +0200616 expires := tr_Expires(int2str(c_def_expires)),
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200617 require := tr_Require(superset("sec-agree")),
618 security_client := tr_Security_client(superset(tr_Security_mechanism("ipsec-3gpp",
619 superset(tr_Param("alg","hmac-sha-1-96"))))),
620 supported := tr_Supported(superset("path", "sec-agree")));
621 var charstring sip_expect_str := log2str(exp_req);
622
623 [] SIP.receive(exp_req) -> value g_rx_sip_req {
624 var template (value) PDU_SIP_Response tx_resp;
625 var Via via;
626 var CallidString sip_call_id;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200627 var template (value) From from_addr;
628 var template (value) To to_addr;
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200629 var WwwAuthenticate wwwAuthenticate;
630 var Security_server security_server;
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200631 var template (value) Require require := ts_Require({"sec-agree"});
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200632 var template (value) Supported supported := ts_Supported({"sec-agree"});
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200633 var integer sip_seq_nr;
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200634
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200635 if (early_ret == IMS_REG_EARLY_RET_BEFORE_Initial_100Trying) {
636 return; /* Done */
637 }
638
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200639 sip_call_id := g_rx_sip_req.msgHeader.callId.callid;
640 via := g_rx_sip_req.msgHeader.via;
641 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "rport", "1234"); /* TODO: set remote src port of the REGISTER */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200642 from_addr := g_rx_sip_req.msgHeader.fromField;
643 to_addr := g_rx_sip_req.msgHeader.toField;
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200644 sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
645
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200646 /* Tx 100 Tyring */
647 tx_resp := ts_SIP_Response_Trying(sip_call_id,
648 from_addr,
649 to_addr,
650 via,
651 sip_seq_nr,
652 "REGISTER",
653 allow := omit,
654 server := g_pars.server_name,
655 userAgent := omit);
656 SIP.send(tx_resp);
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200657
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200658 /* Validate P-Access-Network-Info: rfc7315 6.4:
659 * "3GPP will use the P-Access-Network-Info header field to
660 * carry relatively sensitive information like the cell ID. Therefore,
661 * the information MUST NOT be sent outside of the 3GPP domain.""
662 * [...] "the sensitive information carried in the
663 * P-Access-Network-Info header field MUST NOT be sent in any initial
664 * unauthenticated and unprotected requests (e.g., REGISTER)."
665 */
666 f_ims_validate_register_P_Access_Network_info(g_rx_sip_req, exp_present := false);
667
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200668 f_ims_validate_register_contact(g_rx_sip_req.msgHeader.contact);
669 f_ims_parse_register_contact(g_rx_sip_req.msgHeader.contact);
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200670 f_ims_parse_security_client(g_rx_sip_req.msgHeader.security_client);
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200671
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200672 if (early_ret == IMS_REG_EARLY_RET_BEFORE_Initial_401Unauthorized) {
673 return; /* Done */
674 }
675
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200676 if (not exp_auth_resync) {
677 /* Delay ipsec setup in ip xfrm, since there will be another
678 * 1st REGISTER with potentially new ports coming in later. */
679 f_ims_setup_ipsec();
680 }
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200681
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200682 to_addr.toParams := f_sip_param_set(to_addr.toParams, "tag", f_sip_rand_tag());
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200683 wwwAuthenticate := f_gen_WwwAuthenticate();
684 security_server := f_gen_Security_server();
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200685
686 /* Tx 401 Unauthorized */
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200687 tx_resp := ts_SIP_Response_Unauthorized(sip_call_id,
688 from_addr,
689 to_addr,
690 via,
691 wwwAuthenticate,
692 sip_seq_nr,
693 "REGISTER",
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200694 p_associated_uri := g_pars.subscr.p_associated_uri,
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200695 security_server := security_server,
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200696 server := g_pars.server_name,
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200697 supported := supported,
698 userAgent := omit);
699 SIP.send(tx_resp);
700
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200701 if (exp_auth_resync) {
702 /* Now we should receive a new non-protected REGISTER
703 * with Authoritzation containing auts in base64: */
704 var template (present) Authorization authorization :=
705 f_tr_Authorization_AKAv1MD5(wwwAuthenticate,
706 g_pars.subscr.imsi & "@" & g_pars.realm,
707 f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri));
708 exp_req :=
709 tr_SIP_REGISTER(g_pars.registrar_sip_req_uri,
710 ?,
711 tr_From(),
712 tr_To(),
713 tr_Via_from(f_tr_HostPort(via.viaBody[0].sentBy.host, via.viaBody[0].sentBy.portField)),
714 authorization := authorization);
715 SIP.receive(exp_req) -> value g_rx_sip_req;
716
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200717 if (early_ret == IMS_REG_EARLY_RET_BEFORE_Resync_401Unauthorized) {
718 return; /* Done */
719 }
720
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200721 via := g_rx_sip_req.msgHeader.via;
722 from_addr := g_rx_sip_req.msgHeader.fromField;
723 to_addr := g_rx_sip_req.msgHeader.toField;
724 sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
725
726 /* Tx 100 Tyring */
727 tx_resp := ts_SIP_Response_Trying(sip_call_id,
728 from_addr,
729 to_addr,
730 via,
731 sip_seq_nr,
732 "REGISTER",
733 allow := omit,
734 server := g_pars.server_name,
735 userAgent := omit);
736 SIP.send(tx_resp);
737
738 f_sip_param_match_value_or_fail(g_rx_sip_req.msgHeader.authorization.body.digestResponse,
739 "auts", f_sip_str_quote(enc_MIME_Base64(g_pars.subscr.auth.auts)));
740 f_ims_validate_register_P_Access_Network_info(g_rx_sip_req, exp_present := false);
741 f_ims_validate_register_contact(g_rx_sip_req.msgHeader.contact);
742 f_ims_parse_register_contact(g_rx_sip_req.msgHeader.contact);
743 f_ims_parse_security_client(g_rx_sip_req.msgHeader.security_client);
744 f_ims_setup_ipsec();
745
746 security_server := f_gen_Security_server();
747
748 /* Tx again 401 Unauthorized, this time our AMI interface will accept it: */
749 tx_resp := ts_SIP_Response_Unauthorized(sip_call_id,
750 from_addr,
751 to_addr,
752 via,
753 wwwAuthenticate,
754 sip_seq_nr,
755 "REGISTER",
756 p_associated_uri := g_pars.subscr.p_associated_uri,
757 security_server := security_server,
758 server := g_pars.server_name,
759 supported := supported,
760 userAgent := omit);
761 SIP.send(tx_resp);
762 }
763
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200764 /* Now we should receive a new REGISTER over ipsec: */
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200765 as_IMS_2nd_register(wwwAuthenticate, early_ret := early_ret);
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200766 }
767 [fail_others] as_SIP_fail_resp(sip_expect_str);
768 [fail_others] as_SIP_fail_req(sip_expect_str);
769
770}
771
772/* Peer is issuing 2nd register, accept it: */
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200773altstep as_IMS_2nd_register(WwwAuthenticate wwwAuthenticate,
774 IMS_register_early_return early_ret := IMS_REG_EARLY_RET_BEFORE_None,
775 boolean fail_others := true) runs on IMS_ConnHdlr
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200776{
777 var template (present) Authorization authorization :=
778 f_tr_Authorization_AKAv1MD5(wwwAuthenticate,
779 g_pars.subscr.imsi & "@" & g_pars.realm,
780 f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri));
781 var template (present) PDU_SIP_Request exp_req :=
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200782 tr_SIP_REGISTER(g_pars.registrar_sip_req_uri,
783 ?,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200784 tr_From(),
785 tr_To(),
Pau Espin Pedrol480b53c2024-06-07 19:49:02 +0200786 tr_Via_from(f_tr_HostPort(g_pars.subscr.remote_sip_host, g_pars.subscr.ipsec_remote_port_s)),
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200787 authorization := authorization);
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200788 var charstring sip_expect_str := log2str(exp_req);
789
790 [] SIP.receive(exp_req) -> value g_rx_sip_req {
791 var template (value) PDU_SIP_Response tx_resp;
792 var Via via;
793 var CallidString sip_call_id;
794 var template (value) From from_addr;
795 var template (value) To to_addr;
796 var template (value) Require require := ts_Require({"sec-agree"});
797 var template (value) Supported supported := ts_Supported({"sec-agree"});
798 var integer sip_seq_nr;
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200799
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200800 if (early_ret == IMS_REG_EARLY_RET_BEFORE_Protected_100Trying) {
801 return; /* Done */
802 }
803
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200804 sip_call_id := g_rx_sip_req.msgHeader.callId.callid;
805 via := g_rx_sip_req.msgHeader.via;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200806 from_addr := g_rx_sip_req.msgHeader.fromField;
807 to_addr := g_rx_sip_req.msgHeader.toField;
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200808 sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
809
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200810 /* Tx 100 Trying */
811 tx_resp := ts_SIP_Response_Trying(sip_call_id,
812 from_addr,
813 to_addr,
814 via,
815 sip_seq_nr,
816 "REGISTER",
817 allow := omit,
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200818 server := g_pars.server_name,
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200819 userAgent := omit);
820 SIP.send(tx_resp);
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200821
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +0200822 /* Validate Digest Response: */
823 f_ims_validate_Authorization_AKAv1MD5_Response(g_rx_sip_req.msgHeader.authorization, "REGISTER");
824
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200825 /* Validate P-Access-Network-Info: */
826 f_ims_validate_register_P_Access_Network_info(g_rx_sip_req, exp_present := true);
827
Pau Espin Pedrol50db4c92024-06-11 18:02:52 +0200828 if (early_ret == IMS_REG_EARLY_RET_BEFORE_Protected_200OK) {
829 return; /* Done */
830 }
831
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200832 g_pars.subscr.p_associated_uri := valueof(ts_P_Associated_Uri({
Pau Espin Pedrol4e6672c2024-05-22 17:03:53 +0200833 ts_P_Assoc_uri_spec(ts_NameAddr(ts_SipUrl(ts_HostPort(g_pars.realm),
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200834 ts_UserInfo(g_pars.subscr.msisdn),
Pau Espin Pedrol4e6672c2024-05-22 17:03:53 +0200835 scheme := "sip"))),
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200836 ts_P_Assoc_uri_spec(ts_NameAddr(ts_SipUrl(ts_HostPort(g_pars.subscr.msisdn),
Pau Espin Pedrol4e6672c2024-05-22 17:03:53 +0200837 omit,
838 scheme := "tel"))),
839 ts_P_Assoc_uri_spec(g_rx_sip_req.msgHeader.toField.addressField.nameAddr)
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200840 }));
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200841 f_ims_validate_register_contact(g_rx_sip_req.msgHeader.contact);
842 f_ims_parse_register_contact(g_rx_sip_req.msgHeader.contact);
843 g_pars.subscr.registered_contact := g_rx_sip_req.msgHeader.contact;
Pau Espin Pedrol4e6672c2024-05-22 17:03:53 +0200844
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200845 /* Tx 200 OK */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200846 to_addr.toParams := f_sip_param_set(to_addr.toParams, "tag", f_sip_rand_tag());
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200847 tx_resp := ts_SIP_Response(sip_call_id,
848 from_addr,
849 to_addr,
850 "REGISTER", 200,
851 sip_seq_nr,
852 "OK",
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200853 via,
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200854 p_associated_uri := g_pars.subscr.p_associated_uri,
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200855 require := require,
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200856 server := g_pars.server_name,
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200857 supported := supported,
858 userAgent := omit);
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200859 SIP.send(tx_resp);
860 }
861 [fail_others] as_SIP_fail_resp(sip_expect_str);
862 [fail_others] as_SIP_fail_req(sip_expect_str);
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200863}
864
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200865/* Peer wants to unregister, accept it: */
866altstep as_IMS_unregister(boolean fail_others := true) runs on IMS_ConnHdlr
867{
868 var template (present) PDU_SIP_Request exp_req :=
869 tr_SIP_REGISTER(g_pars.registrar_sip_req_uri,
870 ?,
871 tr_From(),
872 tr_To(),
Pau Espin Pedrol480b53c2024-06-07 19:49:02 +0200873 tr_Via_from(f_tr_HostPort(g_pars.subscr.remote_sip_host, g_pars.subscr.ipsec_remote_port_s)),
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200874 expires := tr_Expires(int2str(0)),
875 require := tr_Require(superset("sec-agree")),
876 security_client := tr_Security_client(superset(tr_Security_mechanism("ipsec-3gpp",
877 superset(tr_Param("alg","hmac-sha-1-96"))))),
878 supported := tr_Supported(superset("path", "sec-agree")));
879 var charstring sip_expect_str := log2str(exp_req);
880
881 [] SIP.receive(exp_req) -> value g_rx_sip_req {
882 var template (value) PDU_SIP_Response tx_resp;
883 var Via via;
884 var CallidString sip_call_id;
885 var Contact contact;
886 var template (value) From from_addr;
887 var template (value) To to_addr;
888 var template (value) CommaParam_List digestCln ;
889 var template (value) WwwAuthenticate wwwAuthenticate;
890 var template (value) Security_server security_server;
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200891 var template (value) Require require := ts_Require({"sec-agree"});
892 var template (value) Supported supported := ts_Supported({"sec-agree"});
893 var template (present) Authorization authorization;
894 var integer sip_seq_nr;
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200895
896 sip_call_id := g_rx_sip_req.msgHeader.callId.callid;
897 via := g_rx_sip_req.msgHeader.via;
898 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "rport", "1234"); /* TODO: set remote src port of the REGISTER */
899 from_addr := g_rx_sip_req.msgHeader.fromField;
900 to_addr := g_rx_sip_req.msgHeader.toField;
901 sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
902
903 contact := g_rx_sip_req.msgHeader.contact;
904 f_ims_validate_register_contact(contact);
905
Pau Espin Pedrolf1963b32024-06-07 17:07:16 +0200906 /* Validate P-Access-Network-Info: 3GPP TS 24.229 5.1.2A.1.1
907 * "If available to the UE (as defined in the access technology specific annexes for each access technology), the UE shall
908 * insert a P-Access-Network-Info header field into any request for a dialog, any subsequent request (except CANCEL
909 * requests) or response (except CANCEL responses) within a dialog or any request for a standalone method (see
910 * subclause 7.2A.4). Insertion of the P-Access-Network-Info header field into the ACK request is optional."
911 */
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200912 f_ims_validate_register_P_Access_Network_info(g_rx_sip_req, exp_present := true);
913
Pau Espin Pedrolf1963b32024-06-07 17:07:16 +0200914
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200915 /* Tx 100 Tyring */
916 tx_resp := ts_SIP_Response_Trying(sip_call_id,
917 from_addr,
918 to_addr,
919 via,
920 sip_seq_nr,
921 "REGISTER",
922 allow := omit,
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200923 server := g_pars.server_name,
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200924 userAgent := omit);
925 SIP.send(tx_resp);
926
927 /* Change all Contact parameters to expires=0: */
928 for (var integer i := 0; i < lengthof(contact.contactBody.contactAddresses); i := i + 1) {
929 contact.contactBody.contactAddresses[i].contactParams := valueof({ ts_Param("expires", "0") });
930 }
Pau Espin Pedrol5acf7c62024-06-06 19:23:08 +0200931 g_pars.subscr.registered_contact := omit;
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200932 /* Tx 200 OK */
933 to_addr.toParams := f_sip_param_set(to_addr.toParams, "tag", f_sip_rand_tag());
934 tx_resp := ts_SIP_Response(sip_call_id,
935 from_addr,
936 to_addr,
937 "REGISTER", 200,
938 sip_seq_nr,
939 "OK",
940 via,
941 contact := contact,
942 p_associated_uri := g_pars.subscr.p_associated_uri,
943 require := require,
Pau Espin Pedrol8e1bdd42024-06-10 20:13:48 +0200944 server := g_pars.server_name,
Pau Espin Pedrol586eec52024-06-04 19:07:33 +0200945 supported := supported,
946 userAgent := omit);
947 SIP.send(tx_resp);
948 }
949 [fail_others] as_SIP_fail_resp(sip_expect_str);
950 [fail_others] as_SIP_fail_req(sip_expect_str);
951}
952
Pau Espin Pedrol5acf7c62024-06-06 19:23:08 +0200953function f_IMS_mt_call_setup() runs on IMS_ConnHdlr
954{
955 var template (value) PDU_SIP_Request req;
956 var template (present) PDU_SIP_Response exp;
957 var template (present) From from_addr_exp;
958 var template (present) To to_addr_exp;
959 var Via via;
960 var charstring tx_sdp := f_gen_sdp();
961 var default d_trying, d_ringing;
962 var charstring branch_value;
963 var Contact calling_contact;
964
965 /* RFC 3261 8.1.1.3 From */
966 g_pars.subscr.cp.from_addr := valueof(ts_From(g_pars.subscr.cp.calling.addr, g_pars.subscr.cp.calling.params));
967 g_pars.subscr.cp.from_addr.fromParams := f_sip_param_set(g_pars.subscr.cp.from_addr.fromParams, "tag", f_sip_rand_tag());
968 g_pars.subscr.cp.to_addr := valueof(ts_To(g_pars.subscr.cp.called.addr, g_pars.subscr.cp.called.params));
969 from_addr_exp := tr_From(tr_Addr_Union_from_val(g_pars.subscr.cp.from_addr.addressField), *);
970 to_addr_exp := tr_To(tr_Addr_Union_from_val(g_pars.subscr.cp.to_addr.addressField), *);
971 branch_value := f_sip_gen_branch(f_sip_Addr_Union_to_str(g_pars.subscr.cp.from_addr.addressField),
972 f_sip_Addr_Union_to_str(valueof(g_pars.subscr.cp.to_addr.addressField)),
973 g_pars.subscr.cp.sip_call_id,
974 g_pars.subscr.cp.sip_seq_nr);
975 via := g_pars.local_via;
976 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
977
978 calling_contact := valueof(ts_Contact({
979 ts_ContactAddress(g_pars.subscr.cp.calling.addr, omit)
980 }));
981
982 req := ts_SIP_INVITE(g_pars.subscr.cp.sip_call_id,
983 g_pars.subscr.cp.from_addr,
984 g_pars.subscr.cp.to_addr,
985 via,
986 calling_contact,
987 g_pars.subscr.cp.sip_seq_nr,
988 body := tx_sdp);
989
990 SIP.send(req);
991
992 /* Conditionally match and accept 100 Trying. */
993 exp := tr_SIP_Response_Trying(g_pars.subscr.cp.sip_call_id,
994 from_addr_exp,
995 to_addr_exp,
996 f_tr_Via_response(via),
997 g_pars.subscr.cp.sip_seq_nr, "INVITE");
998 d_trying := activate(as_SIP_ignore_resp(exp));
999
1000 /* Conditionally match and accept 180 Ringing */
1001 exp := tr_SIP_Response_Ringing(g_pars.subscr.cp.sip_call_id,
1002 from_addr_exp,
1003 to_addr_exp,
1004 f_tr_Via_response(via),
1005 g_pars.subscr.cp.sip_seq_nr, "INVITE");
1006 d_ringing := activate(as_SIP_ignore_resp(exp));
1007
1008 /* Wait for OK answer */
1009 exp := tr_SIP_Response(
1010 g_pars.subscr.cp.sip_call_id,
1011 from_addr_exp,
1012 to_addr_exp,
1013 f_tr_Via_response(via),
1014 *,
1015 "INVITE", 200,
1016 g_pars.subscr.cp.sip_seq_nr, "OK",
1017 body := ?);
1018 as_SIP_expect_resp(exp, fail_others := false);
1019
1020 deactivate(d_trying);
1021 deactivate(d_ringing);
1022
1023 /* Update To with the tags received from peer: */
1024 g_pars.subscr.cp.to_addr := g_rx_sip_resp.msgHeader.toField;
1025
1026 /* Transmit ACK */
1027 g_pars.subscr.cp.sip_seq_nr := g_pars.subscr.cp.sip_seq_nr + 1;
1028 req := ts_SIP_ACK(g_pars.subscr.cp.sip_call_id,
1029 g_pars.subscr.cp.from_addr,
1030 g_pars.subscr.cp.to_addr,
1031 via,
1032 g_pars.subscr.cp.sip_seq_nr,
1033 omit);
1034 SIP.send(req);
1035 g_pars.subscr.cp.sip_seq_nr := g_pars.subscr.cp.sip_seq_nr + 1;
1036}
1037
1038/* Tx BYE: */
1039function f_IMS_do_call_hangup() runs on IMS_ConnHdlr
1040{
1041 var template (value) PDU_SIP_Request req;
1042 var template (present) PDU_SIP_Response exp_resp;
1043 var Via via;
1044 var charstring branch_value;
1045
1046 branch_value := f_sip_gen_branch(f_sip_Addr_Union_to_str(g_pars.subscr.cp.from_addr.addressField),
1047 f_sip_Addr_Union_to_str(valueof(g_pars.subscr.cp.to_addr.addressField)),
1048 g_pars.subscr.cp.sip_call_id,
1049 g_pars.subscr.cp.sip_seq_nr);
1050
1051 via := g_pars.local_via;
1052 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
1053
1054 /* Transmit ACK */
1055 req := ts_SIP_BYE(g_pars.subscr.cp.sip_call_id,
1056 g_pars.subscr.cp.from_addr,
1057 g_pars.subscr.cp.to_addr,
1058 via,
1059 g_pars.subscr.cp.sip_seq_nr,
1060 omit);
1061 SIP.send(req);
1062
1063 /* Wait for OK answer */
1064 exp_resp := tr_SIP_Response(
1065 g_pars.subscr.cp.sip_call_id,
1066 g_pars.subscr.cp.from_addr,
1067 tr_To(tr_Addr_Union_from_val(g_pars.subscr.cp.to_addr.addressField), *),
1068 f_tr_Via_response(via),
1069 *,
1070 "BYE", 200,
1071 g_pars.subscr.cp.sip_seq_nr, "OK");
1072 as_SIP_expect_resp(exp_resp);
1073
1074 g_pars.subscr.cp.sip_seq_nr := g_pars.subscr.cp.sip_seq_nr + 1;
1075}
1076
Pau Espin Pedrol901cede2024-05-30 13:03:42 +02001077private function f_ConnHdlr_parse_initial_SIP_INVITE(PDU_SIP_Request rx_sip_req) runs on IMS_ConnHdlr
1078{
1079 f_SDP_decodeMessage(rx_sip_req.messageBody, g_pars.subscr.cp.peer_sdp);
1080 log("Rx Initial MO INVITE decoded SDP: ", g_pars.subscr.cp.peer_sdp);
1081
1082 /* Obtain params: */
1083 g_pars.subscr.cp.sip_call_id := rx_sip_req.msgHeader.callId.callid;
1084 g_pars.subscr.cp.from_addr := rx_sip_req.msgHeader.fromField;
1085 g_pars.subscr.cp.to_addr := rx_sip_req.msgHeader.toField;
1086 g_pars.subscr.cp.to_addr.toParams := f_sip_param_set(g_pars.subscr.cp.to_addr.toParams, "tag", f_sip_rand_tag());
1087 g_pars.subscr.cp.sip_seq_nr := rx_sip_req.msgHeader.cSeq.seqNumber;
1088}
1089
1090/* Peer is calling us, accept it: */
1091altstep as_IMS_mo_call_accept(boolean exp_update_to_direct_rtp := false,
1092 boolean fail_others := true) runs on IMS_ConnHdlr
1093{
1094 var template (present) PDU_SIP_Request exp_req :=
1095 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(ts_SipUrl_from_Addr_Union(g_pars.subscr.cp.called.addr)),
1096 ?,
Pau Espin Pedrol09087012024-06-04 18:07:48 +02001097 tr_From(tr_Addr_Union_from_val(g_pars.subscr.cp.calling.addr), *),
Pau Espin Pedrol901cede2024-05-30 13:03:42 +02001098 tr_To(tr_Addr_Union_from_val(g_pars.subscr.cp.called.addr), *),
Pau Espin Pedrol480b53c2024-06-07 19:49:02 +02001099 tr_Via_from(f_tr_HostPort(g_pars.subscr.remote_sip_host, g_pars.subscr.ipsec_remote_port_s)),
Pau Espin Pedrol901cede2024-05-30 13:03:42 +02001100 ?, ?);
1101 var charstring sip_expect_str := log2str(exp_req);
1102
1103 [] SIP.receive(exp_req) -> value g_rx_sip_req {
1104 var template (value) PDU_SIP_Response tx_resp;
1105 var Via via;
1106 var charstring tx_sdp;
1107
1108 /* Obtain params: */
1109 f_ConnHdlr_parse_initial_SIP_INVITE(g_rx_sip_req);
1110 via := g_rx_sip_req.msgHeader.via;
1111
Pau Espin Pedrolf1963b32024-06-07 17:07:16 +02001112 f_ims_validate_register_P_Access_Network_info(g_rx_sip_req, exp_present := true);
1113
Pau Espin Pedrol901cede2024-05-30 13:03:42 +02001114
1115 /* Tx 180 Ringing */
1116 tx_resp := ts_SIP_Response_Ringing(g_pars.subscr.cp.sip_call_id,
1117 g_pars.subscr.cp.from_addr,
1118 g_pars.subscr.cp.to_addr,
1119 via,
1120 g_pars.subscr.cp.sip_seq_nr);
1121 SIP.send(tx_resp);
1122
1123 /* Tx 200 OK */
1124 tx_sdp := f_gen_sdp();
1125 tx_resp := ts_SIP_Response(g_pars.subscr.cp.sip_call_id,
1126 g_pars.subscr.cp.from_addr,
1127 g_pars.subscr.cp.to_addr,
1128 "INVITE", 200,
1129 g_pars.subscr.cp.sip_seq_nr,
1130 "OK",
1131 via,
1132 body := tx_sdp);
1133 SIP.send(tx_resp);
1134
1135 /* Wait for ACK */
1136 exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(ts_SipUrl_from_Addr_Union(g_pars.subscr.cp.called.addr)),
1137 g_pars.subscr.cp.sip_call_id,
1138 g_pars.subscr.cp.from_addr,
1139 g_pars.subscr.cp.to_addr,
1140 f_tr_Via_response(via),
1141 g_pars.subscr.cp.sip_seq_nr, *);
1142 as_SIP_expect_req(exp_req);
1143 }
1144 [fail_others] as_SIP_fail_resp(sip_expect_str);
1145 [fail_others] as_SIP_fail_req(sip_expect_str);
1146
1147}
1148
1149/* Call is terminated by peer: */
1150altstep as_IMS_exp_call_hangup(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on IMS_ConnHdlr
1151{
1152 var template (present) PDU_SIP_Request exp_req :=
1153 tr_SIP_BYE(f_tr_SipUrl_opt_defport(ts_SipUrl_from_Addr_Union(g_pars.subscr.cp.called.addr)),
1154 g_pars.subscr.cp.sip_call_id,
1155 g_pars.subscr.cp.from_addr,
1156 g_pars.subscr.cp.to_addr,
Pau Espin Pedrol480b53c2024-06-07 19:49:02 +02001157 tr_Via_from(f_tr_HostPort(g_pars.subscr.remote_sip_host, g_pars.subscr.ipsec_remote_port_s)),
Pau Espin Pedrol901cede2024-05-30 13:03:42 +02001158 exp_seq_nr);
1159 var charstring sip_expect_str := log2str(exp_req);
1160
1161 [] SIP.receive(exp_req) -> value g_rx_sip_req {
1162 var template (value) PDU_SIP_Response tx_resp;
1163 var charstring tx_sdp;
1164 var Via via;
1165
1166 /* Update parameters: */
1167 g_pars.subscr.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
1168 /* "branch" has changed: */
1169 via := g_rx_sip_req.msgHeader.via;
1170
1171 /* Tx 200 OK */
1172 tx_sdp := f_gen_sdp();
1173 tx_resp := ts_SIP_Response(g_pars.subscr.cp.sip_call_id,
1174 g_pars.subscr.cp.from_addr,
1175 g_pars.subscr.cp.to_addr,
1176 "BYE", 200,
1177 g_pars.subscr.cp.sip_seq_nr,
1178 "OK",
1179 via,
1180 body := tx_sdp);
1181 SIP.send(tx_resp);
1182 }
1183 [fail_others] as_SIP_fail_resp(sip_expect_str);
1184 [fail_others] as_SIP_fail_req(sip_expect_str);
1185}
1186
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +02001187}