Pau Espin Pedrol | 4669b61 | 2023-10-23 17:43:14 +0200 | [diff] [blame] | 1 | module DIAMETER_ts29_273_Templates { |
| 2 | |
| 3 | /* (C) 2023 by sysmocom s.f.m.c. GmbH <info@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 | * |
| 11 | * Templates for AVPs and messages for TS 29.273 |
| 12 | */ |
| 13 | |
| 14 | import from General_Types all; |
| 15 | import from DIAMETER_Types all; |
| 16 | import from DIAMETER_Templates all; |
Pau Espin Pedrol | 7399673 | 2023-10-23 18:16:21 +0200 | [diff] [blame] | 17 | import from DIAMETER_rfc5447_Templates all; |
Pau Espin Pedrol | 4669b61 | 2023-10-23 17:43:14 +0200 | [diff] [blame] | 18 | import from Osmocom_Types all; |
| 19 | import from Misc_Helpers all; |
| 20 | |
| 21 | /* 3GPP TS 29.273 Section 8.2 */ |
| 22 | const uint32_t c_DIAMETER_3GPP_SWx_AID := 16777265; |
| 23 | /* 3GPP TS 29.273 Section 9 */ |
| 24 | const uint32_t c_DIAMETER_3GPP_S6b_AID := 16777272; |
| 25 | |
Pau Espin Pedrol | 7399673 | 2023-10-23 18:16:21 +0200 | [diff] [blame] | 26 | /* 5.2.3.3 MIP6-Feature-Vector bits */ |
| 27 | const uint64_t DIA_TS29_373_MIP6_Feature_Vector_PMIP6_SUPPORTED := hex2int('0000010000000000'H); |
| 28 | const uint64_t DIA_TS29_373_MIP6_Feature_Vector_ASSIGN_LOCAL_IP := hex2int('0000080000000000'H); |
| 29 | const uint64_t DIA_TS29_373_MIP6_Feature_Vector_MIP4_SUPPORTED := hex2int('0000100000000000'H); |
| 30 | const uint64_t DIA_TS29_373_MIP6_Feature_Vector_OPTIMIZED_IDLE_MODE_MOBILITY := hex2int('0000200000000000'H); |
| 31 | const uint64_t DIA_TS29_373_MIP6_Feature_Vector_GTPv2_SUPPORTED := hex2int('0000400000000000'H); |
| 32 | |
Pau Espin Pedrol | 4669b61 | 2023-10-23 17:43:14 +0200 | [diff] [blame] | 33 | /******************************* |
| 34 | * SWx 3GPP TS 29.273 section 8 |
| 35 | *******************************/ |
| 36 | |
| 37 | /* SIP-Auth-Data-Item , 3GPP TS 29.273 8.2.3.9 */ |
| 38 | template (present) GenericAVP tr_AVP_3GPP_SIPAuthDataItem(template (present) uint32_t num := ?) := { |
| 39 | avp := { |
| 40 | avp_header := tr_DIA_Hdr_3GPP(c_AVP_Code_CxDx_3GPP_SIP_Auth_Data_Item), |
| 41 | avp_data := { |
| 42 | avp_CxDx_3GPP_SIP_Auth_Data_Item := superset( |
| 43 | //tr_AVP_3GPP_SIPItemNumber(num), /* Optional */ |
| 44 | tr_AVP_3GPP_SIPAuthScheme(?)//, /* Optional */ |
| 45 | //tr_AVP_3GPP_SIPAuthenticate(?), /* Optional */ |
| 46 | //tr_AVP_3GPP_SIPAuthorization(?), /* Optional */ |
| 47 | //tr_AVP_3GPP_SIPAuthContext(?), /* Optional */ |
| 48 | //tr_AVP_3GPP_ConfidentialityKey(?), /* Optional */ |
| 49 | //tr_AVP_3GPP_IntegrityKey(?) /* Optional */ |
| 50 | /* TODO: |
| 51 | [ SIP-Digest-Authenticate ] |
| 52 | [ Framed-IP-Address ] |
| 53 | [ Framed-IPv6-Prefix ] |
| 54 | [ Framed-Interface-Id ] |
| 55 | [ Line-Identifier ] |
| 56 | *[AVP] |
| 57 | */ |
| 58 | ) |
| 59 | } |
| 60 | } |
| 61 | } |
| 62 | template (value) GenericAVP ts_AVP_3GPP_SIPAuthDataItem(uint32_t num, OCT16 rand, OCT16 ik, OCT16 ck, OCT16 autn, OCT14 auts) := { |
| 63 | avp := { |
| 64 | avp_header := ts_DIA_Hdr_3GPP(c_AVP_Code_CxDx_3GPP_SIP_Auth_Data_Item), |
| 65 | avp_data := { |
| 66 | avp_CxDx_3GPP_SIP_Auth_Data_Item := { |
| 67 | ts_AVP_3GPP_SIPItemNumber(num), |
| 68 | ts_AVP_3GPP_SIPAuthScheme(char2oct("Digest-AKAv1-MD5")), |
| 69 | ts_AVP_3GPP_SIPAuthenticate(rand & autn), |
| 70 | ts_AVP_3GPP_SIPAuthorization(rand & auts), |
| 71 | ts_AVP_3GPP_SIPAuthContext(char2oct("foobar")), |
| 72 | ts_AVP_3GPP_ConfidentialityKey(ck), |
| 73 | ts_AVP_3GPP_IntegrityKey(ik) |
| 74 | /* TODO: |
| 75 | [ SIP-Digest-Authenticate ] |
| 76 | [ Framed-IP-Address ] |
| 77 | [ Framed-IPv6-Prefix ] |
| 78 | [ Framed-Interface-Id ] |
| 79 | [ Line-Identifier ] |
| 80 | *[AVP] |
| 81 | */ |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | /* Multimedia-Auth-Request, 3GPP TS 29.273 8.2.2.1 Authentication Procedure */ |
| 88 | template (present) PDU_DIAMETER |
| 89 | tr_DIA_SWx_MAR(template (present) hexstring imsi := ?, |
| 90 | template (present) octetstring sess_id := ?, |
| 91 | template (present) charstring orig_host := ?, |
| 92 | template (present) charstring orig_realm := ?, |
| 93 | template (present) charstring dest_realm := ?, |
| 94 | template (present) UINT32 hbh_id := ?, |
| 95 | template (present) UINT32 ete_id := ?) := |
| 96 | tr_DIAMETER(flags := '1???????'B, |
| 97 | cmd_code := Multimedia_Auth, |
| 98 | app_id := int2oct(c_DIAMETER_3GPP_SWx_AID, 4), |
| 99 | hbh_id := hbh_id, ete_id := ete_id, |
| 100 | avps := superset( |
| 101 | tr_AVP_SessionId(sess_id), |
| 102 | tr_AVP_VendorSpecAppId(?, ?), |
| 103 | tr_AVP_AuthSessionState(NO_STATE_MAINTAINED), |
| 104 | tr_AVP_OriginHost(orig_host), |
| 105 | tr_AVP_OriginRealm(orig_realm), |
| 106 | tr_AVP_DestinationRealm(dest_realm), |
| 107 | tr_AVP_UserNameImsi(imsi), |
| 108 | tr_AVP_3GPP_SIPAuthDataItem(?), |
| 109 | tr_AVP_3GPP_SIPNumAuthDataItems(?) |
| 110 | )); |
| 111 | |
| 112 | /* Multimedia-Auth-Answer, 3GPP TS 29.273 8.2.2.1 Authentication Procedure */ |
| 113 | template (value) PDU_DIAMETER |
| 114 | ts_DIA_SWx_MAA(template (value) hexstring imsi, |
| 115 | template (value) GenericAVP sip_auth_data_item, |
| 116 | template (value) uint32_t vendor_app_id := c_DIAMETER_3GPP_SWx_AID, |
| 117 | template (value) octetstring sess_id := c_def_sess_id, |
| 118 | template (value) charstring orig_host := "hss.localdomain", |
| 119 | template (value) charstring orig_realm := "localdomain", |
| 120 | template (value) UINT32 hbh_id := '00000000'O, |
| 121 | template (value) UINT32 ete_id := '00000000'O) := |
| 122 | ts_DIAMETER(flags := '01000000'B, |
| 123 | cmd_code := Multimedia_Auth, |
| 124 | app_id := int2oct(c_DIAMETER_3GPP_SWx_AID, 4), |
| 125 | hbh_id := hbh_id, |
| 126 | ete_id := ete_id, |
| 127 | avps := { |
| 128 | ts_AVP_SessionId(sess_id), |
| 129 | ts_AVP_VendorSpecAppId(vendor_id_3GPP, valueof(vendor_app_id)), |
| 130 | ts_AVP_ResultCode(DIAMETER_SUCCESS), |
| 131 | ts_AVP_AuthSessionState(NO_STATE_MAINTAINED), |
| 132 | ts_AVP_OriginHost(orig_host), |
| 133 | ts_AVP_OriginRealm(orig_realm), |
| 134 | ts_AVP_UserNameImsi(valueof(imsi)), |
| 135 | sip_auth_data_item, |
| 136 | ts_AVP_3GPP_SIPNumAuthDataItems(1) |
| 137 | }); |
| 138 | |
| 139 | /* Server-Assignment-Request, |
| 140 | * 3GPP TS 29.273 8.1.2.2.2 UE/PDN Registration/DeRegistration Notification |
| 141 | * 3GPP TS 29.273 8.2.2.3 Non-3GPP IP Access Registration Procedure */ |
| 142 | template (present) PDU_DIAMETER |
| 143 | tr_DIA_SWx_SAR(template (present) hexstring imsi := ?, |
| 144 | template (present) octetstring sess_id := ?, |
| 145 | template (present) charstring orig_host := ?, |
| 146 | template (present) charstring orig_realm := ?, |
| 147 | template (present) charstring dest_realm := ?, |
| 148 | template (present) UINT32 hbh_id := ?, |
| 149 | template (present) UINT32 ete_id := ?, |
| 150 | template (present) CxDx_3GPP_Server_Assignment_Type server_ass_type := ?, |
| 151 | template (present) charstring service_selection := ?) := |
| 152 | tr_DIAMETER(flags := '1???????'B, |
| 153 | cmd_code := Server_Assignment, |
| 154 | app_id := int2oct(c_DIAMETER_3GPP_SWx_AID, 4), |
| 155 | hbh_id := hbh_id, ete_id := ete_id, |
| 156 | avps := superset( |
| 157 | tr_AVP_SessionId(sess_id), |
| 158 | tr_AVP_VendorSpecAppId(?, ?), |
| 159 | tr_AVP_AuthSessionState(NO_STATE_MAINTAINED), |
| 160 | tr_AVP_OriginHost(orig_host), |
| 161 | tr_AVP_OriginRealm(orig_realm), |
| 162 | tr_AVP_DestinationRealm(dest_realm), |
| 163 | tr_AVP_UserNameImsi(imsi), |
| 164 | tr_AVP_3GPP_ServerAssignmentType(server_ass_type), |
| 165 | tr_AVP_ServiceSelection(service_selection) |
| 166 | )); |
| 167 | |
| 168 | /* Server-Assignment-Answer, |
| 169 | * 3GPP TS 29.273 8.1.2.2.2 UE/PDN Registration/DeRegistration Notification |
| 170 | * 3GPP TS 29.273 8.2.2.3 Non-3GPP IP Access Registration Procedure */ |
| 171 | template (value) PDU_DIAMETER |
| 172 | ts_DIA_SWx_SAA(template (value) hexstring imsi, |
| 173 | template (value) uint32_t vendor_app_id := c_DIAMETER_3GPP_SWx_AID, |
| 174 | template (value) octetstring sess_id := c_def_sess_id, |
| 175 | template (value) charstring orig_host := "hss.localdomain", |
| 176 | template (value) charstring orig_realm := "localdomain", |
| 177 | template (value) UINT32 hbh_id := '00000000'O, |
| 178 | template (value) UINT32 ete_id := '00000000'O) := |
| 179 | ts_DIAMETER(flags := '01000000'B, |
| 180 | cmd_code := Server_Assignment, |
| 181 | app_id := int2oct(c_DIAMETER_3GPP_SWx_AID, 4), |
| 182 | hbh_id := hbh_id, |
| 183 | ete_id := ete_id, |
| 184 | avps := { |
| 185 | ts_AVP_SessionId(sess_id), |
| 186 | ts_AVP_VendorSpecAppId(vendor_id_3GPP, valueof(vendor_app_id)), |
| 187 | ts_AVP_ResultCode(DIAMETER_SUCCESS), |
| 188 | ts_AVP_AuthSessionState(NO_STATE_MAINTAINED), |
| 189 | ts_AVP_OriginHost(orig_host), |
| 190 | ts_AVP_OriginRealm(orig_realm), |
| 191 | ts_AVP_UserNameImsi(valueof(imsi)) |
| 192 | /* TODO: |
| 193 | * [ Non-3GPP-User-Data ] |
| 194 | * [ 3GPP-AAA-Server-Name ] |
| 195 | * [ OC-Supported-Features ] |
| 196 | * [ OC-OLR ] ] |
| 197 | * *[ Load ] |
| 198 | * *[ Supported-Features ] |
| 199 | */ |
| 200 | }); |
| 201 | |
| 202 | /******************************* |
| 203 | * S6b 3GPP TS 29.273 section 9 |
| 204 | *******************************/ |
| 205 | |
| 206 | /* TS 29.273 9.2.2.5.1 AA-Request (AAR) */ |
Pau Espin Pedrol | 518e24b | 2024-02-23 17:45:08 +0100 | [diff] [blame] | 207 | template (present) PDU_DIAMETER |
| 208 | tr_DIA_S6b_AAR(template (present) charstring username_nai := ?, |
| 209 | template (present) MIPv6_NONE_MIP6_Feature_Vector mip6_feat_vec := ?, |
| 210 | template (present) charstring apn := ?, |
| 211 | template (present) octetstring sess_id := ?, |
| 212 | template (present) charstring orig_host := ?, |
| 213 | template (present) charstring orig_realm := ?, |
| 214 | template (present) charstring dest_realm := ?, |
| 215 | template (present) UINT32 hbh_id := ?, |
| 216 | template (present) UINT32 ete_id := ?) := |
| 217 | tr_DIAMETER(flags := '1???????'B, |
| 218 | cmd_code := Authorize_Authenticate, |
| 219 | app_id := int2oct(c_DIAMETER_3GPP_S6b_AID, 4), |
| 220 | hbh_id := hbh_id, |
| 221 | ete_id := ete_id, |
| 222 | avps := superset( |
| 223 | tr_AVP_SessionId(sess_id), |
| 224 | /* Optional: DRMP, */ |
| 225 | tr_AVP_AuthAppId(int2oct(c_DIAMETER_3GPP_S6b_AID, 4)), |
| 226 | tr_AVP_OriginHost(orig_host), |
| 227 | tr_AVP_OriginRealm(orig_realm), |
| 228 | tr_AVP_DestinationRealm(dest_realm), |
| 229 | tr_AVP_AuthRequestType(AUTHORIZE_ONLY), |
| 230 | tr_AVP_UserName(char2oct_tmpl_present(username_nai)), |
| 231 | tr_AVP_MIP6FeatureVector(mip6_feat_vec), |
| 232 | tr_AVP_ServiceSelection(apn) |
| 233 | /* TODO: Lots other optional */ |
| 234 | )); |
| 235 | |
Pau Espin Pedrol | 4669b61 | 2023-10-23 17:43:14 +0200 | [diff] [blame] | 236 | template (value) PDU_DIAMETER |
Pau Espin Pedrol | 8586b26 | 2024-02-09 18:12:21 +0100 | [diff] [blame] | 237 | ts_DIA_S6b_AAR(template (value) charstring username_nai, |
Pau Espin Pedrol | 7399673 | 2023-10-23 18:16:21 +0200 | [diff] [blame] | 238 | template (value) MIPv6_NONE_MIP6_Feature_Vector mip6_feat_vec, |
Pau Espin Pedrol | 2c2e187 | 2023-10-23 18:31:03 +0200 | [diff] [blame] | 239 | template (value) charstring apn, |
Pau Espin Pedrol | 4669b61 | 2023-10-23 17:43:14 +0200 | [diff] [blame] | 240 | template (value) octetstring sess_id := c_def_sess_id, |
| 241 | template (value) charstring orig_host := "pgw.localdomain", |
| 242 | template (value) charstring orig_realm := "localdomain", |
| 243 | template (value) charstring dest_realm := "localdomain", |
| 244 | template (value) UINT32 hbh_id := '00000000'O, |
| 245 | template (value) UINT32 ete_id := '00000000'O) := |
| 246 | ts_DIAMETER(flags := '11000000'B, |
| 247 | cmd_code := Authorize_Authenticate, |
| 248 | app_id := int2oct(c_DIAMETER_3GPP_S6b_AID, 4), |
| 249 | hbh_id := hbh_id, |
| 250 | ete_id := ete_id, |
| 251 | avps := { |
| 252 | ts_AVP_SessionId(sess_id), |
| 253 | /* Optional: DRMP, */ |
| 254 | ts_AVP_AuthAppId(int2oct(c_DIAMETER_3GPP_S6b_AID, 4)), |
| 255 | ts_AVP_OriginHost(orig_host), |
| 256 | ts_AVP_OriginRealm(orig_realm), |
| 257 | ts_AVP_DestinationRealm(dest_realm), |
| 258 | ts_AVP_AuthRequestType(AUTHORIZE_ONLY), |
Pau Espin Pedrol | 8586b26 | 2024-02-09 18:12:21 +0100 | [diff] [blame] | 259 | ts_AVP_UserName(char2oct(valueof(username_nai))), |
Pau Espin Pedrol | 2c2e187 | 2023-10-23 18:31:03 +0200 | [diff] [blame] | 260 | ts_AVP_MIP6FeatureVector(mip6_feat_vec), |
| 261 | ts_AVP_ServiceSelection(valueof(apn)) |
Pau Espin Pedrol | 4669b61 | 2023-10-23 17:43:14 +0200 | [diff] [blame] | 262 | /* TODO: Lots other optional */ |
| 263 | }); |
| 264 | |
| 265 | /* TS 29.273 9.2.2.2.2 AA-Answer (AAA) */ |
| 266 | template (present) PDU_DIAMETER |
| 267 | tr_DIA_S6b_AAA(template (present) octetstring sess_id := ?, |
| 268 | template (present) charstring orig_host := ?, |
| 269 | template (present) charstring orig_realm := ?, |
| 270 | template (present) charstring dest_realm := ?, |
| 271 | template (present) UINT32 hbh_id := ?, |
| 272 | template (present) UINT32 ete_id := ?, |
| 273 | template (present) CxDx_3GPP_Server_Assignment_Type server_ass_type := ?, |
| 274 | template (present) charstring service_selection := ?) := |
| 275 | tr_DIAMETER(flags := '0???????'B, |
| 276 | cmd_code := Authorize_Authenticate, |
| 277 | app_id := int2oct(c_DIAMETER_3GPP_S6b_AID, 4), |
| 278 | hbh_id := hbh_id, ete_id := ete_id, |
| 279 | avps := superset( |
| 280 | tr_AVP_SessionId(sess_id), |
| 281 | /* Optional: DRMP, */ |
| 282 | tr_AVP_AuthAppId(int2oct(c_DIAMETER_3GPP_S6b_AID, 4)), |
| 283 | tr_AVP_AuthRequestType(AUTHORIZE_ONLY), |
| 284 | tr_AVP_ResultCode(DIAMETER_SUCCESS), |
| 285 | tr_AVP_OriginHost(orig_host), |
| 286 | tr_AVP_OriginRealm(orig_realm) |
| 287 | )); |
| 288 | |
Pau Espin Pedrol | 518e24b | 2024-02-23 17:45:08 +0100 | [diff] [blame] | 289 | template (value) PDU_DIAMETER |
| 290 | ts_DIA_S6b_AAA(template (value) octetstring sess_id := c_def_sess_id, |
| 291 | template (value) charstring orig_host := "aaa.localdomain", |
| 292 | template (value) charstring orig_realm := "localdomain", |
| 293 | template (value) charstring dest_realm := "localdomain", |
| 294 | template (value) UINT32 hbh_id := '00000000'O, |
| 295 | template (value) UINT32 ete_id := '00000000'O) := |
| 296 | ts_DIAMETER(flags := '01000000'B, |
| 297 | cmd_code := Authorize_Authenticate, |
| 298 | app_id := int2oct(c_DIAMETER_3GPP_S6b_AID, 4), |
| 299 | hbh_id := hbh_id, ete_id := ete_id, |
| 300 | avps := { |
| 301 | ts_AVP_SessionId(sess_id), |
| 302 | /* Optional: DRMP, */ |
| 303 | ts_AVP_AuthAppId(int2oct(c_DIAMETER_3GPP_S6b_AID, 4)), |
| 304 | ts_AVP_AuthRequestType(AUTHORIZE_ONLY), |
| 305 | ts_AVP_ResultCode(DIAMETER_SUCCESS), |
| 306 | ts_AVP_OriginHost(orig_host), |
| 307 | ts_AVP_OriginRealm(orig_realm) |
| 308 | }); |
| 309 | |
Pau Espin Pedrol | 4944a5c | 2024-01-24 17:23:01 +0100 | [diff] [blame] | 310 | /* TS 29.273 9.2.2.3.1 Session-Termination-Request (STR) Command, |
| 311 | * Table 9.1.2.3.1/1: S6b Session Termination Request (STR), based on RFC 6733 8.4.1 */ |
| 312 | template (value) PDU_DIAMETER |
| 313 | ts_DIA_S6b_STR(template (value) hexstring imsi, |
| 314 | template (value) BASE_NONE_Termination_Cause term_cause := DIAMETER_LOGOUT, |
| 315 | template (value) octetstring sess_id := c_def_sess_id, |
| 316 | template (value) charstring orig_host := "pgw.localdomain", |
| 317 | template (value) charstring orig_realm := "localdomain", |
| 318 | template (value) charstring dest_realm := "localdomain", |
| 319 | template (value) UINT32 hbh_id := '00000000'O, |
| 320 | template (value) UINT32 ete_id := '00000000'O) := |
| 321 | ts_DIAMETER(flags := '11000000'B, |
| 322 | cmd_code := Session_Termination, |
| 323 | app_id := int2oct(c_DIAMETER_3GPP_S6b_AID, 4), |
| 324 | hbh_id := hbh_id, |
| 325 | ete_id := ete_id, |
| 326 | avps := { |
| 327 | ts_AVP_SessionId(sess_id), |
| 328 | /* Optional: DRMP */ |
| 329 | ts_AVP_AuthAppId(int2oct(c_DIAMETER_3GPP_S6b_AID, 4)), |
| 330 | ts_AVP_OriginHost(orig_host), |
| 331 | ts_AVP_OriginRealm(orig_realm), |
| 332 | ts_AVP_DestinationRealm(dest_realm), |
| 333 | ts_AVP_TerminationCause(term_cause), |
| 334 | ts_AVP_UserNameImsi(valueof(imsi)) |
| 335 | /* Optional: OC-Supported-Features */ |
| 336 | }); |
| 337 | |
| 338 | /* TS 29.273 9.2.2.3.2 Session-Termination-Answer (STA) Command, |
| 339 | * Table 9.1.2.3.1/2: S6b Session Termination Answer (STA), based on RFC 6733 8.4.2 */ |
| 340 | template (present) PDU_DIAMETER |
| 341 | tr_DIA_S6b_STA(template (present) DIAMETER_Resultcode res_code := ?, |
| 342 | template (present) octetstring sess_id := ?, |
| 343 | template (present) charstring orig_host := ?, |
| 344 | template (present) charstring orig_realm := ?, |
| 345 | template (present) charstring dest_realm := ?, |
| 346 | template (present) UINT32 hbh_id := ?, |
| 347 | template (present) UINT32 ete_id := ?) := |
| 348 | tr_DIAMETER(flags := '0???????'B, |
| 349 | cmd_code := Session_Termination, |
| 350 | app_id := int2oct(c_DIAMETER_3GPP_S6b_AID, 4), |
| 351 | hbh_id := hbh_id, ete_id := ete_id, |
| 352 | avps := superset( |
| 353 | tr_AVP_SessionId(sess_id), |
| 354 | /* Optional: DRMP */ |
| 355 | tr_AVP_ResultCode(res_code), |
| 356 | tr_AVP_OriginHost(orig_host), |
| 357 | tr_AVP_OriginRealm(orig_realm) |
| 358 | /* Lots other Optional */ |
| 359 | )); |
| 360 | |
Pau Espin Pedrol | 4669b61 | 2023-10-23 17:43:14 +0200 | [diff] [blame] | 361 | } |