blob: 69107d4bc4eaa07ab54cc476a0601e39182de182 [file] [log] [blame]
Harald Welted27ab242019-07-26 13:45:18 +02001module DIAMETER_Emulation {
2
3/* DIAMETER Emulation, runs on top of DIAMETER_CodecPort. It multiplexes/demultiplexes
4 * the individual IMSIs/subscribers, so there can be separate TTCN-3 components handling
5 * each of them.
6 *
7 * The DIAMETER_Emulation.main() function processes DIAMETER primitives from the DIAMETER
8 * socket via the DIAMETER_CodecPort, and dispatches them to the per-IMSI components.
9 *
10 * For each new IMSI, the DiameterOps.create_cb() is called. It can create
11 * or resolve a TTCN-3 component, and returns a component reference to which that IMSI
12 * is routed/dispatched.
13 *
14 * If a pre-existing component wants to register to handle a future inbound IMSI, it can
15 * do so by registering an "expect" with the expected IMSI.
16 *
17 * Inbound DIAMETER messages without IMSI (such as RESET-IND/ACK) are dispatched to
18 * the DiameterOps.unitdata_cb() callback, which is registered with an argument to the
19 * main() function below.
20 *
Vadim Yanitskiyb46f01e2021-12-06 03:23:13 +030021 * Alternatively, all inbound DIAMETER PDUs can be routed to a single component
22 * regardless of the IMSI. This is called 'raw' mode and can be achieved by
23 * setting the 'raw' field in DIAMETEROps to true.
24 *
Harald Welted27ab242019-07-26 13:45:18 +020025 * (C) 2019 by Harald Welte <laforge@gnumonks.org>
26 * All rights reserved.
27 *
28 * Released under the terms of GNU General Public License, Version 2 or
29 * (at your option) any later version.
30 *
31 * SPDX-License-Identifier: GPL-2.0-or-later
32 */
33
34import from DIAMETER_CodecPort all;
35import from DIAMETER_CodecPort_CtrlFunct all;
36import from DIAMETER_Types all;
37import from DIAMETER_Templates all;
38import from Osmocom_Types all;
39import from IPL4asp_Types all;
Pau Espin Pedrol518e24b2024-02-23 17:45:08 +010040import from TCCConversion_Functions all;
Harald Welte61f73d52020-04-26 21:41:12 +020041import from Native_Functions all;
Harald Welted27ab242019-07-26 13:45:18 +020042
43type hexstring IMSI;
44
Harald Welted01b5d02020-04-26 22:05:53 +020045/* notify the recipient that a Capability Exchange happened */
46type record DiameterCapabilityExchgInd {
47 PDU_DIAMETER rx,
48 PDU_DIAMETER tx
49};
50
Harald Welted27ab242019-07-26 13:45:18 +020051type component DIAMETER_ConnHdlr {
52 port DIAMETER_Conn_PT DIAMETER;
53 /* procedure based port to register for incoming connections */
54 port DIAMETEREM_PROC_PT DIAMETER_PROC;
55}
56
57/* port between individual per-connection components and this dispatcher */
58type port DIAMETER_Conn_PT message {
Harald Welte0e038082019-08-18 19:38:54 +020059 inout PDU_DIAMETER;
Harald Welted27ab242019-07-26 13:45:18 +020060} with { extension "internal" };
61
62/* global test port e.g. for non-imsi/conn specific messages */
63type port DIAMETER_PT message {
Harald Welted01b5d02020-04-26 22:05:53 +020064 inout PDU_DIAMETER, DiameterCapabilityExchgInd;
Harald Welted27ab242019-07-26 13:45:18 +020065} with { extension "internal" };
66
67
68/* represents a single DIAMETER Association */
69type record AssociationData {
70 DIAMETER_ConnHdlr comp_ref,
71 hexstring imsi optional
72};
73
Pau Espin Pedroldb017f42023-08-25 19:22:25 +020074/* represents a single DIAMETER message identified by ete_id field */
75type record ETEIDData {
76 DIAMETER_ConnHdlr comp_ref,
77 UINT32 ete_id optional
78};
79
Harald Welted27ab242019-07-26 13:45:18 +020080type component DIAMETER_Emulation_CT {
81 /* Port facing to the UDP SUT */
82 port DIAMETER_CODEC_PT DIAMETER;
83 /* All DIAMETER_ConnHdlr DIAMETER ports connect here
84 * DIAMETER_Emulation_CT.main needs to figure out what messages
85 * to send where with CLIENT.send() to vc_conn */
86 port DIAMETER_Conn_PT DIAMETER_CLIENT;
87 /* currently tracked connections */
Pau Espin Pedrolb335f012022-04-11 12:07:41 +020088 var AssociationData DiameterAssocTable[256];
Pau Espin Pedroldb017f42023-08-25 19:22:25 +020089 /* Forward reply messages not containing IMSI to correct client port */
90 var ETEIDData DiameterETEIDTable[256];
Harald Welted27ab242019-07-26 13:45:18 +020091 /* pending expected CRCX */
Pau Espin Pedrolb335f012022-04-11 12:07:41 +020092 var ExpectData DiameterExpectTable[256];
Harald Welted27ab242019-07-26 13:45:18 +020093 /* procedure based port to register for incoming connections */
94 port DIAMETEREM_PROC_PT DIAMETER_PROC;
95 /* test port for unit data messages */
96 port DIAMETER_PT DIAMETER_UNIT;
97
98 var charstring g_diameter_id;
99 var integer g_diameter_conn_id := -1;
100}
101
102type function DIAMETERCreateCallback(PDU_DIAMETER msg, hexstring imsi, charstring id)
103runs on DIAMETER_Emulation_CT return DIAMETER_ConnHdlr;
104
105type function DIAMETERUnitdataCallback(PDU_DIAMETER msg)
106runs on DIAMETER_Emulation_CT return template PDU_DIAMETER;
107
108type record DIAMETEROps {
109 DIAMETERCreateCallback create_cb,
Vadim Yanitskiyb46f01e2021-12-06 03:23:13 +0300110 DIAMETERUnitdataCallback unitdata_cb,
111 /* If true, this parameter disables IMSI based routing, so that all incoming
112 * PDUs get routed to a single component connected via the DIAMETER_UNIT port. */
113 boolean raw
Harald Welted27ab242019-07-26 13:45:18 +0200114}
115
116type record DIAMETER_conn_parameters {
117 HostName remote_ip,
118 PortNumber remote_sctp_port,
119 HostName local_ip,
Harald Welte61f73d52020-04-26 21:41:12 +0200120 PortNumber local_sctp_port,
121 charstring origin_host,
122 charstring origin_realm,
Pau Espin Pedrol33b47492022-03-08 17:43:01 +0100123 uint32_t auth_app_id optional,
124 uint32_t vendor_app_id optional
Harald Welted27ab242019-07-26 13:45:18 +0200125}
126
127function tr_DIAMETER_RecvFrom_R(template PDU_DIAMETER msg)
128runs on DIAMETER_Emulation_CT return template DIAMETER_RecvFrom {
129 var template DIAMETER_RecvFrom mrf := {
130 connId := g_diameter_conn_id,
131 remName := ?,
132 remPort := ?,
133 locName := ?,
134 locPort := ?,
135 msg := msg
136 }
137 return mrf;
138}
139
140private function f_imsi_known(hexstring imsi)
141runs on DIAMETER_Emulation_CT return boolean {
142 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200143 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
144 if (DiameterAssocTable[i].imsi == imsi) {
Harald Welted27ab242019-07-26 13:45:18 +0200145 return true;
146 }
147 }
148 return false;
149}
150
151private function f_comp_known(DIAMETER_ConnHdlr client)
152runs on DIAMETER_Emulation_CT return boolean {
153 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200154 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
155 if (DiameterAssocTable[i].comp_ref == client) {
Harald Welted27ab242019-07-26 13:45:18 +0200156 return true;
157 }
158 }
159 return false;
160}
161
162private function f_comp_by_imsi(hexstring imsi)
163runs on DIAMETER_Emulation_CT return DIAMETER_ConnHdlr {
164 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200165 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
166 if (DiameterAssocTable[i].imsi == imsi) {
167 return DiameterAssocTable[i].comp_ref;
Harald Welted27ab242019-07-26 13:45:18 +0200168 }
169 }
170 setverdict(fail, "DIAMETER Association Table not found by IMSI", imsi);
171 mtc.stop;
172}
173
174private function f_imsi_by_comp(DIAMETER_ConnHdlr client)
175runs on DIAMETER_Emulation_CT return hexstring {
176 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200177 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
178 if (DiameterAssocTable[i].comp_ref == client) {
179 return DiameterAssocTable[i].imsi;
Harald Welted27ab242019-07-26 13:45:18 +0200180 }
181 }
182 setverdict(fail, "DIAMETER Association Table not found by component ", client);
183 mtc.stop;
184}
185
186private function f_imsi_table_add(DIAMETER_ConnHdlr comp_ref, hexstring imsi)
187runs on DIAMETER_Emulation_CT {
188 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200189 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
190 if (not isvalue(DiameterAssocTable[i].imsi)) {
191 DiameterAssocTable[i].imsi := imsi;
192 DiameterAssocTable[i].comp_ref := comp_ref;
Harald Welted27ab242019-07-26 13:45:18 +0200193 return;
194 }
195 }
196 testcase.stop("DIAMETER Association Table full!");
197}
198
199private function f_imsi_table_del(DIAMETER_ConnHdlr comp_ref, hexstring imsi)
200runs on DIAMETER_Emulation_CT {
201 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200202 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
203 if (DiameterAssocTable[i].comp_ref == comp_ref and
204 DiameterAssocTable[i].imsi == imsi) {
205 DiameterAssocTable[i].imsi := omit;
206 DiameterAssocTable[i].comp_ref := null;
Harald Welted27ab242019-07-26 13:45:18 +0200207 return;
208 }
209 }
210 setverdict(fail, "DIAMETER Association Table: Couldn't find to-be-deleted entry!");
211 mtc.stop;
212}
213
Harald Welted27ab242019-07-26 13:45:18 +0200214private function f_imsi_table_init()
215runs on DIAMETER_Emulation_CT {
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200216 for (var integer i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
217 DiameterAssocTable[i].comp_ref := null;
218 DiameterAssocTable[i].imsi := omit;
Harald Welted27ab242019-07-26 13:45:18 +0200219 }
220}
221
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200222/* End-to-End ID table matching. */
223private function f_ete_id_known(UINT32 ete_id)
224runs on DIAMETER_Emulation_CT return boolean {
225 var integer i;
226 for (i := 0; i < sizeof(DiameterETEIDTable); i := i+1) {
227 if (DiameterETEIDTable[i].ete_id == ete_id) {
228 return true;
229 }
230 }
231 return false;
232}
233
234private function f_comp_by_ete_id(UINT32 ete_id)
235runs on DIAMETER_Emulation_CT return DIAMETER_ConnHdlr {
236 var integer i;
237 for (i := 0; i < sizeof(DiameterETEIDTable); i := i+1) {
238 if (DiameterETEIDTable[i].ete_id == ete_id) {
239 return DiameterETEIDTable[i].comp_ref;
240 }
241 }
242 setverdict(fail, "DIAMETER ETEID Table not found by ete_id", ete_id);
243 mtc.stop;
244}
245
246private function f_eteid_table_add(DIAMETER_ConnHdlr comp_ref, UINT32 ete_id)
247runs on DIAMETER_Emulation_CT {
248 var integer i;
249 for (i := 0; i < sizeof(DiameterETEIDTable); i := i+1) {
250 if (not isvalue(DiameterETEIDTable[i].ete_id)) {
251 DiameterETEIDTable[i].ete_id := ete_id;
252 DiameterETEIDTable[i].comp_ref := comp_ref;
253 return;
254 }
255 }
256 testcase.stop("DIAMETER ETEID Table full!");
257}
258
259private function f_eteid_table_del(DIAMETER_ConnHdlr comp_ref, UINT32 ete_id)
260runs on DIAMETER_Emulation_CT {
261 var integer i;
262 for (i := 0; i < sizeof(DiameterETEIDTable); i := i+1) {
263 if (DiameterETEIDTable[i].comp_ref == comp_ref and
264 DiameterETEIDTable[i].ete_id == ete_id) {
265 DiameterETEIDTable[i].ete_id := omit;
266 DiameterETEIDTable[i].comp_ref := null;
267 return;
268 }
269 }
270 setverdict(fail, "DIAMETER ETEID Table: Couldn't find to-be-deleted entry!");
271 mtc.stop;
272}
273
274
275private function f_eteid_table_init()
276runs on DIAMETER_Emulation_CT {
277 for (var integer i := 0; i < sizeof(DiameterETEIDTable); i := i+1) {
278 DiameterETEIDTable[i].comp_ref := null;
279 DiameterETEIDTable[i].ete_id := omit;
280 }
281}
282
Harald Welted27ab242019-07-26 13:45:18 +0200283function f_DIAMETER_get_imsi(PDU_DIAMETER pdu) return template (omit) IMSI
284{
285 var template (omit) AVP imsi_avp;
286
287 imsi_avp := f_DIAMETER_get_avp(pdu, c_AVP_Code_BASE_NONE_User_Name);
288 if (istemplatekind(imsi_avp, "omit")) {
Harald Weltef9fb63e2020-04-26 18:07:19 +0200289 var template (omit) AVP sid_avp;
290 sid_avp := f_DIAMETER_get_avp(pdu, c_AVP_Code_DCC_NONE_Subscription_Id);
291 if (istemplatekind(sid_avp, "omit")) {
292 return omit;
293 }
294 var AVP_Grouped grp := valueof(sid_avp.avp_data.avp_DCC_NONE_Subscription_Id);
Pau Espin Pedrolb8cd34a2022-05-18 16:34:12 +0200295 if (not match(grp[0], tr_AVP_SubcrIdType(END_USER_IMSI))) {
Harald Weltef9fb63e2020-04-26 18:07:19 +0200296 return omit;
297 }
298 return str2hex(oct2char(grp[1].avp.avp_data.avp_DCC_NONE_Subscription_Id_Data));
Harald Welted27ab242019-07-26 13:45:18 +0200299 } else {
300 var octetstring imsi_oct := valueof(imsi_avp.avp_data.avp_BASE_NONE_User_Name);
Pau Espin Pedrol518e24b2024-02-23 17:45:08 +0100301 var charstring imsi_str := oct2char(imsi_oct);
302 /* Username may be a NAI instead of IMSI: "<IMSI>@nai.epc.mnc<MNC>.mcc<MCC>.3gppnetwork.org" */
303 var integer pos := f_strstr(imsi_str, "@");
304 if (pos != -1) {
305 imsi_str := substr(imsi_str, 0, pos);
306 }
307 return str2hex(imsi_str);
Harald Welted27ab242019-07-26 13:45:18 +0200308 }
309}
310
311private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := omit) := {
312 sinfo_stream := omit,
313 sinfo_ppid := ppid,
314 remSocks := omit,
315 assocId := omit
316};
317
318private template PortEvent tr_SctpAssocChange := {
319 sctpEvent := {
320 sctpAssocChange := ?
321 }
322}
323private template PortEvent tr_SctpPeerAddrChange := {
324 sctpEvent := {
325 sctpPeerAddrChange := ?
326 }
327}
328
329private function f_diameter_xceive(template (value) PDU_DIAMETER tx,
330 template PDU_DIAMETER rx_t := ?)
331runs on DIAMETER_Emulation_CT return PDU_DIAMETER {
332 timer T := 10.0;
333 var DIAMETER_RecvFrom mrf;
334
335 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, tx));
Vadim Yanitskiy672a41e2023-02-14 04:52:56 +0700336 T.start;
Harald Welted27ab242019-07-26 13:45:18 +0200337 alt {
338 [] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(rx_t)) -> value mrf { }
Vadim Yanitskiy3148a0e2023-02-11 09:27:42 +0700339 [] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(?)) -> value mrf {
340 setverdict(fail, "Rx unexpected DIAMETER PDU: ", mrf);
341 mtc.stop;
342 }
Harald Welted27ab242019-07-26 13:45:18 +0200343 [] DIAMETER.receive(tr_SctpAssocChange) { repeat; }
344 [] DIAMETER.receive(tr_SctpPeerAddrChange) { repeat; }
345 [] T.timeout {
346 setverdict(fail, "Timeout waiting for ", rx_t);
347 mtc.stop;
348 }
349 }
350 return mrf.msg;
351}
352
353function main(DIAMETEROps ops, DIAMETER_conn_parameters p, charstring id) runs on DIAMETER_Emulation_CT {
Vadim Yanitskiyb94e4592023-02-11 08:09:09 +0700354 var boolean server_mode := p.remote_sctp_port == -1;
Harald Welted27ab242019-07-26 13:45:18 +0200355 var Result res;
356 g_diameter_id := id;
357 f_imsi_table_init();
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200358 f_eteid_table_init();
Harald Welted27ab242019-07-26 13:45:18 +0200359 f_expect_table_init();
360
361 map(self:DIAMETER, system:DIAMETER_CODEC_PT);
Vadim Yanitskiyb94e4592023-02-11 08:09:09 +0700362 if (server_mode) {
Harald Welted27ab242019-07-26 13:45:18 +0200363 res := DIAMETER_CodecPort_CtrlFunct.f_IPL4_listen(DIAMETER, p.local_ip, p.local_sctp_port, { sctp := valueof(ts_SCTP) });
364 } else {
365 res := DIAMETER_CodecPort_CtrlFunct.f_IPL4_connect(DIAMETER, p.remote_ip, p.remote_sctp_port,
366 p.local_ip, p.local_sctp_port, -1, { sctp := valueof(ts_SCTP) });
367 }
368 if (not ispresent(res.connId)) {
369 setverdict(fail, "Could not connect DIAMETER socket, check your configuration");
370 mtc.stop;
371 }
372 g_diameter_conn_id := res.connId;
373
Vadim Yanitskiyb94e4592023-02-11 08:09:09 +0700374 /* If in client mode, send CER immediately */
375 if (not server_mode) {
376 var template (value) PDU_DIAMETER req;
377 var PDU_DIAMETER rsp;
378
379 req := ts_DIA_CER(f_inet_addr(p.local_ip), p.vendor_app_id,
380 orig_host := p.origin_host, orig_realm := p.origin_realm);
381 rsp := f_diameter_xceive(req, tr_DIAMETER_A(Capabilities_Exchange, req.application_id));
382 /* notify our user that the CER->CEA exchange has happened */
383 DIAMETER_UNIT.send(DiameterCapabilityExchgInd:{rx := rsp, tx := valueof(req)});
384 }
385
Harald Welted27ab242019-07-26 13:45:18 +0200386 while (true) {
387 var DIAMETER_ConnHdlr vc_conn;
Harald Welted27ab242019-07-26 13:45:18 +0200388 var template IMSI imsi_t;
389 var hexstring imsi;
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200390 var UINT32 ete_id;
Harald Welted27ab242019-07-26 13:45:18 +0200391 var DIAMETER_RecvFrom mrf;
392 var PDU_DIAMETER msg;
393 var charstring vlr_name, mme_name;
394 var PortEvent port_evt;
395
396 alt {
397 [] DIAMETER.receive(PortEvent:{connOpened := ?}) -> value port_evt {
398 g_diameter_conn_id := port_evt.connOpened.connId;
399 }
400 [] DIAMETER.receive(PortEvent:?) { }
401 /* DIAMETER from client */
402 [] DIAMETER_CLIENT.receive(PDU_DIAMETER:?) -> value msg sender vc_conn {
403 /* Pass message through */
404 /* TODO: check which ConnectionID client has allocated + store in table? */
405 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, msg));
406 }
407
408 /* handle CER/CEA handshake */
409 [] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(tr_DIAMETER_R(cmd_code := Capabilities_Exchange))) -> value mrf {
410 var template (value) PDU_DIAMETER resp;
Pau Espin Pedrol33b47492022-03-08 17:43:01 +0100411 resp := f_ts_DIA_CEA(mrf.msg.hop_by_hop_id, mrf.msg.end_to_end_id, p.origin_host,
412 p.origin_realm, f_inet_addr(p.local_ip), p.auth_app_id, p.vendor_app_id);
Harald Welted27ab242019-07-26 13:45:18 +0200413 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, resp));
Harald Welted01b5d02020-04-26 22:05:53 +0200414 /* notify our user that the CER->CEA exchange has happened */
415 DIAMETER_UNIT.send(DiameterCapabilityExchgInd:{rx:=mrf.msg, tx:=valueof(resp)});
Harald Welted27ab242019-07-26 13:45:18 +0200416 }
Vadim Yanitskiy3f7a6dc2021-12-11 04:13:59 +0300417 /* handle DWR/DWA ping-pong */
418 [] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(tr_DIA_DWR)) -> value mrf {
419 var template (value) PDU_DIAMETER resp;
420 resp := ts_DIA_DWA('00000001'O, p.origin_host, p.origin_realm,
421 hbh_id := mrf.msg.hop_by_hop_id,
422 ete_id := mrf.msg.end_to_end_id);
423 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, valueof(resp)));
424 }
Harald Welted27ab242019-07-26 13:45:18 +0200425
Vadim Yanitskiyb46f01e2021-12-06 03:23:13 +0300426 /* DIAMETER from the test suite */
427 [ops.raw] DIAMETER_UNIT.receive(PDU_DIAMETER:?) -> value msg {
428 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, msg));
429 }
430 /* DIAMETER from remote peer (raw mode) */
431 [ops.raw] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(?)) -> value mrf {
432 DIAMETER_UNIT.send(mrf.msg);
433 }
434 /* DIAMETER from remote peer (IMSI based routing) */
435 [not ops.raw] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(?)) -> value mrf {
Harald Welted27ab242019-07-26 13:45:18 +0200436 imsi_t := f_DIAMETER_get_imsi(mrf.msg);
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200437 ete_id := mrf.msg.end_to_end_id;
438 if (f_ete_id_known(ete_id)) {
439 vc_conn := f_comp_by_ete_id(ete_id);
440 /* The ete_id is a single-time expect: */
441 f_eteid_table_del(vc_conn, ete_id);
442 DIAMETER_CLIENT.send(mrf.msg) to vc_conn;
443 } else if (isvalue(imsi_t)) {
Harald Welted27ab242019-07-26 13:45:18 +0200444 imsi := valueof(imsi_t);
445 if (f_imsi_known(imsi)) {
446 vc_conn := f_comp_by_imsi(imsi);
447 DIAMETER_CLIENT.send(mrf.msg) to vc_conn;
448 } else {
449 vc_conn := ops.create_cb.apply(mrf.msg, imsi, id);
450 f_imsi_table_add(vc_conn, imsi);
451 DIAMETER_CLIENT.send(mrf.msg) to vc_conn;
452 }
453 } else {
454 /* message contained no IMSI; is not IMSI-oriented */
455 var template PDU_DIAMETER resp := ops.unitdata_cb.apply(mrf.msg);
456 if (isvalue(resp)) {
457 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, valueof(resp)));
458 }
459 }
460 }
461 [] DIAMETER.receive(tr_SctpAssocChange) { }
462 [] DIAMETER.receive(tr_SctpPeerAddrChange) { }
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200463 [] DIAMETER_PROC.getcall(DIAMETEREM_register_imsi:{?,?}) -> param(imsi, vc_conn) {
Harald Welted27ab242019-07-26 13:45:18 +0200464 f_create_expect(imsi, vc_conn);
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200465 DIAMETER_PROC.reply(DIAMETEREM_register_imsi:{imsi, vc_conn}) to vc_conn;
466 }
467 [] DIAMETER_PROC.getcall(DIAMETEREM_register_eteid:{?,?}) -> param(ete_id, vc_conn) {
468 f_eteid_table_add(vc_conn, ete_id);
469 DIAMETER_PROC.reply(DIAMETEREM_register_eteid:{ete_id, vc_conn}) to vc_conn;
Harald Welted27ab242019-07-26 13:45:18 +0200470 }
471
472 }
473
474 }
475}
476
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200477/* "E2E ID Expect" Handling */
478type record ExpectDataE2EID {
479 UINT32 ete_id optional,
480 DIAMETER_ConnHdlr vc_conn
481}
482
483signature DIAMETEREM_register_eteid(in UINT32 ete_id, in DIAMETER_ConnHdlr hdlr);
484
485/* client/conn_hdlr side function to use procedure port to create expect in emulation */
486function f_diameter_expect_eteid(UINT32 ete_id) runs on DIAMETER_ConnHdlr {
487 DIAMETER_PROC.call(DIAMETEREM_register_eteid:{ete_id, self}) {
488 [] DIAMETER_PROC.getreply(DIAMETEREM_register_eteid:{?,?}) {};
489 }
490}
491
492/* "IMSI Expect" Handling */
Harald Welted27ab242019-07-26 13:45:18 +0200493
494type record ExpectData {
495 hexstring imsi optional,
496 DIAMETER_ConnHdlr vc_conn
497}
498
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200499signature DIAMETEREM_register_imsi(in hexstring imsi, in DIAMETER_ConnHdlr hdlr);
Harald Welted27ab242019-07-26 13:45:18 +0200500
Pau Espin Pedrol69730a22022-04-11 12:00:53 +0200501/* Function that can be used as create_cb and will use the expect table */
Harald Welted27ab242019-07-26 13:45:18 +0200502function ExpectedCreateCallback(PDU_DIAMETER msg, hexstring imsi, charstring id)
503runs on DIAMETER_Emulation_CT return DIAMETER_ConnHdlr {
504 var DIAMETER_ConnHdlr ret := null;
505 var integer i;
506
507 for (i := 0; i < sizeof(DiameterExpectTable); i := i+1) {
508 if (not ispresent(DiameterExpectTable[i].imsi)) {
509 continue;
510 }
511 if (imsi == DiameterExpectTable[i].imsi) {
512 ret := DiameterExpectTable[i].vc_conn;
513 /* Release this entry */
514 DiameterExpectTable[i].imsi := omit;
515 DiameterExpectTable[i].vc_conn := null;
516 log("Found Expect[", i, "] for ", msg, " handled at ", ret);
517 return ret;
518 }
519 }
520 setverdict(fail, "Couldn't find Expect for ", msg);
521 mtc.stop;
522}
523
524private function f_create_expect(hexstring imsi, DIAMETER_ConnHdlr hdlr)
525runs on DIAMETER_Emulation_CT {
526 var integer i;
527
528 /* Check an entry like this is not already presnt */
529 for (i := 0; i < sizeof(DiameterExpectTable); i := i+1) {
530 if (imsi == DiameterExpectTable[i].imsi) {
531 setverdict(fail, "IMSI already present", imsi);
532 mtc.stop;
533 }
534 }
535 for (i := 0; i < sizeof(DiameterExpectTable); i := i+1) {
536 if (not ispresent(DiameterExpectTable[i].imsi)) {
537 DiameterExpectTable[i].imsi := imsi;
538 DiameterExpectTable[i].vc_conn := hdlr;
539 log("Created Expect[", i, "] for ", imsi, " to be handled at ", hdlr);
540 return;
541 }
542 }
543 testcase.stop("No space left in DiameterExpectTable")
544}
545
546/* client/conn_hdlr side function to use procedure port to create expect in emulation */
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200547function f_diameter_expect_imsi(hexstring imsi) runs on DIAMETER_ConnHdlr {
548 DIAMETER_PROC.call(DIAMETEREM_register_imsi:{imsi, self}) {
549 [] DIAMETER_PROC.getreply(DIAMETEREM_register_imsi:{?,?}) {};
Harald Welted27ab242019-07-26 13:45:18 +0200550 }
551}
552
553private function f_expect_table_init()
554runs on DIAMETER_Emulation_CT {
555 var integer i;
556 for (i := 0; i < sizeof(DiameterExpectTable); i := i + 1) {
557 DiameterExpectTable[i].imsi := omit;
558 }
559}
560
561function DummyUnitdataCallback(PDU_DIAMETER msg)
562runs on DIAMETER_Emulation_CT return template PDU_DIAMETER {
563 log("Ignoring DIAMETER ", msg);
564 return omit;
565}
566
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200567type port DIAMETEREM_PROC_PT procedure {
568 inout DIAMETEREM_register_imsi;
569 inout DIAMETEREM_register_eteid;
570} with { extension "internal" };
Harald Welted27ab242019-07-26 13:45:18 +0200571
Harald Welted01b5d02020-04-26 22:05:53 +0200572function f_diameter_wait_capability(DIAMETER_PT pt)
573{
574 /* Wait for the Capability Exchange with the DUT */
575 timer T := 10.0;
576 T.start;
577 alt {
578 [] pt.receive(DiameterCapabilityExchgInd:?) {}
579 [] pt.receive {
580 setverdict(fail, "Unexpected receive waiting for DiameterCapabilityExchgInd");
581 mtc.stop;
582 }
583 [] T.timeout {
584 setverdict(fail, "Timeout waiting for DiameterCapabilityExchgInd");
585 mtc.stop;
586 }
587 }
588}
589
590
Harald Welted27ab242019-07-26 13:45:18 +0200591}