blob: e5698fd507893ec9ffc3a1aeb19a19f66d2efc24 [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;
Vadim Yanitskiy741aa572024-06-12 06:16:43 +070042import from SCTP_Templates all;
Harald Welted27ab242019-07-26 13:45:18 +020043
44type hexstring IMSI;
45
Harald Welted01b5d02020-04-26 22:05:53 +020046/* notify the recipient that a Capability Exchange happened */
47type record DiameterCapabilityExchgInd {
48 PDU_DIAMETER rx,
49 PDU_DIAMETER tx
50};
51
Harald Welted27ab242019-07-26 13:45:18 +020052type component DIAMETER_ConnHdlr {
53 port DIAMETER_Conn_PT DIAMETER;
54 /* procedure based port to register for incoming connections */
55 port DIAMETEREM_PROC_PT DIAMETER_PROC;
56}
57
58/* port between individual per-connection components and this dispatcher */
59type port DIAMETER_Conn_PT message {
Harald Welte0e038082019-08-18 19:38:54 +020060 inout PDU_DIAMETER;
Harald Welted27ab242019-07-26 13:45:18 +020061} with { extension "internal" };
62
63/* global test port e.g. for non-imsi/conn specific messages */
64type port DIAMETER_PT message {
Harald Welted01b5d02020-04-26 22:05:53 +020065 inout PDU_DIAMETER, DiameterCapabilityExchgInd;
Harald Welted27ab242019-07-26 13:45:18 +020066} with { extension "internal" };
67
68
69/* represents a single DIAMETER Association */
70type record AssociationData {
71 DIAMETER_ConnHdlr comp_ref,
72 hexstring imsi optional
73};
74
Pau Espin Pedroldb017f42023-08-25 19:22:25 +020075/* represents a single DIAMETER message identified by ete_id field */
76type record ETEIDData {
77 DIAMETER_ConnHdlr comp_ref,
78 UINT32 ete_id optional
79};
80
Harald Welted27ab242019-07-26 13:45:18 +020081type component DIAMETER_Emulation_CT {
82 /* Port facing to the UDP SUT */
83 port DIAMETER_CODEC_PT DIAMETER;
84 /* All DIAMETER_ConnHdlr DIAMETER ports connect here
85 * DIAMETER_Emulation_CT.main needs to figure out what messages
86 * to send where with CLIENT.send() to vc_conn */
87 port DIAMETER_Conn_PT DIAMETER_CLIENT;
88 /* currently tracked connections */
Pau Espin Pedrolb335f012022-04-11 12:07:41 +020089 var AssociationData DiameterAssocTable[256];
Pau Espin Pedroldb017f42023-08-25 19:22:25 +020090 /* Forward reply messages not containing IMSI to correct client port */
91 var ETEIDData DiameterETEIDTable[256];
Harald Welted27ab242019-07-26 13:45:18 +020092 /* pending expected CRCX */
Pau Espin Pedrolb335f012022-04-11 12:07:41 +020093 var ExpectData DiameterExpectTable[256];
Harald Welted27ab242019-07-26 13:45:18 +020094 /* procedure based port to register for incoming connections */
95 port DIAMETEREM_PROC_PT DIAMETER_PROC;
96 /* test port for unit data messages */
97 port DIAMETER_PT DIAMETER_UNIT;
98
99 var charstring g_diameter_id;
100 var integer g_diameter_conn_id := -1;
101}
102
103type function DIAMETERCreateCallback(PDU_DIAMETER msg, hexstring imsi, charstring id)
104runs on DIAMETER_Emulation_CT return DIAMETER_ConnHdlr;
105
106type function DIAMETERUnitdataCallback(PDU_DIAMETER msg)
107runs on DIAMETER_Emulation_CT return template PDU_DIAMETER;
108
109type record DIAMETEROps {
110 DIAMETERCreateCallback create_cb,
Vadim Yanitskiyb46f01e2021-12-06 03:23:13 +0300111 DIAMETERUnitdataCallback unitdata_cb,
112 /* If true, this parameter disables IMSI based routing, so that all incoming
113 * PDUs get routed to a single component connected via the DIAMETER_UNIT port. */
114 boolean raw
Harald Welted27ab242019-07-26 13:45:18 +0200115}
116
117type record DIAMETER_conn_parameters {
118 HostName remote_ip,
119 PortNumber remote_sctp_port,
120 HostName local_ip,
Harald Welte61f73d52020-04-26 21:41:12 +0200121 PortNumber local_sctp_port,
122 charstring origin_host,
123 charstring origin_realm,
Pau Espin Pedrol33b47492022-03-08 17:43:01 +0100124 uint32_t auth_app_id optional,
125 uint32_t vendor_app_id optional
Harald Welted27ab242019-07-26 13:45:18 +0200126}
127
128function tr_DIAMETER_RecvFrom_R(template PDU_DIAMETER msg)
129runs on DIAMETER_Emulation_CT return template DIAMETER_RecvFrom {
130 var template DIAMETER_RecvFrom mrf := {
131 connId := g_diameter_conn_id,
132 remName := ?,
133 remPort := ?,
134 locName := ?,
135 locPort := ?,
136 msg := msg
137 }
138 return mrf;
139}
140
141private function f_imsi_known(hexstring imsi)
142runs on DIAMETER_Emulation_CT return boolean {
143 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200144 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
145 if (DiameterAssocTable[i].imsi == imsi) {
Harald Welted27ab242019-07-26 13:45:18 +0200146 return true;
147 }
148 }
149 return false;
150}
151
152private function f_comp_known(DIAMETER_ConnHdlr client)
153runs on DIAMETER_Emulation_CT return boolean {
154 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200155 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
156 if (DiameterAssocTable[i].comp_ref == client) {
Harald Welted27ab242019-07-26 13:45:18 +0200157 return true;
158 }
159 }
160 return false;
161}
162
163private function f_comp_by_imsi(hexstring imsi)
164runs on DIAMETER_Emulation_CT return DIAMETER_ConnHdlr {
165 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200166 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
167 if (DiameterAssocTable[i].imsi == imsi) {
168 return DiameterAssocTable[i].comp_ref;
Harald Welted27ab242019-07-26 13:45:18 +0200169 }
170 }
171 setverdict(fail, "DIAMETER Association Table not found by IMSI", imsi);
172 mtc.stop;
173}
174
175private function f_imsi_by_comp(DIAMETER_ConnHdlr client)
176runs on DIAMETER_Emulation_CT return hexstring {
177 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200178 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
179 if (DiameterAssocTable[i].comp_ref == client) {
180 return DiameterAssocTable[i].imsi;
Harald Welted27ab242019-07-26 13:45:18 +0200181 }
182 }
183 setverdict(fail, "DIAMETER Association Table not found by component ", client);
184 mtc.stop;
185}
186
187private function f_imsi_table_add(DIAMETER_ConnHdlr comp_ref, hexstring imsi)
188runs on DIAMETER_Emulation_CT {
189 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200190 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
191 if (not isvalue(DiameterAssocTable[i].imsi)) {
192 DiameterAssocTable[i].imsi := imsi;
193 DiameterAssocTable[i].comp_ref := comp_ref;
Harald Welted27ab242019-07-26 13:45:18 +0200194 return;
195 }
196 }
197 testcase.stop("DIAMETER Association Table full!");
198}
199
200private function f_imsi_table_del(DIAMETER_ConnHdlr comp_ref, hexstring imsi)
201runs on DIAMETER_Emulation_CT {
202 var integer i;
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200203 for (i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
204 if (DiameterAssocTable[i].comp_ref == comp_ref and
205 DiameterAssocTable[i].imsi == imsi) {
206 DiameterAssocTable[i].imsi := omit;
207 DiameterAssocTable[i].comp_ref := null;
Harald Welted27ab242019-07-26 13:45:18 +0200208 return;
209 }
210 }
211 setverdict(fail, "DIAMETER Association Table: Couldn't find to-be-deleted entry!");
212 mtc.stop;
213}
214
Harald Welted27ab242019-07-26 13:45:18 +0200215private function f_imsi_table_init()
216runs on DIAMETER_Emulation_CT {
Pau Espin Pedrole4361702022-04-11 11:59:40 +0200217 for (var integer i := 0; i < sizeof(DiameterAssocTable); i := i+1) {
218 DiameterAssocTable[i].comp_ref := null;
219 DiameterAssocTable[i].imsi := omit;
Harald Welted27ab242019-07-26 13:45:18 +0200220 }
221}
222
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200223/* End-to-End ID table matching. */
224private function f_ete_id_known(UINT32 ete_id)
225runs on DIAMETER_Emulation_CT return boolean {
226 var integer i;
227 for (i := 0; i < sizeof(DiameterETEIDTable); i := i+1) {
228 if (DiameterETEIDTable[i].ete_id == ete_id) {
229 return true;
230 }
231 }
232 return false;
233}
234
235private function f_comp_by_ete_id(UINT32 ete_id)
236runs on DIAMETER_Emulation_CT return DIAMETER_ConnHdlr {
237 var integer i;
238 for (i := 0; i < sizeof(DiameterETEIDTable); i := i+1) {
239 if (DiameterETEIDTable[i].ete_id == ete_id) {
240 return DiameterETEIDTable[i].comp_ref;
241 }
242 }
243 setverdict(fail, "DIAMETER ETEID Table not found by ete_id", ete_id);
244 mtc.stop;
245}
246
247private function f_eteid_table_add(DIAMETER_ConnHdlr comp_ref, UINT32 ete_id)
248runs on DIAMETER_Emulation_CT {
249 var integer i;
250 for (i := 0; i < sizeof(DiameterETEIDTable); i := i+1) {
251 if (not isvalue(DiameterETEIDTable[i].ete_id)) {
252 DiameterETEIDTable[i].ete_id := ete_id;
253 DiameterETEIDTable[i].comp_ref := comp_ref;
254 return;
255 }
256 }
257 testcase.stop("DIAMETER ETEID Table full!");
258}
259
260private function f_eteid_table_del(DIAMETER_ConnHdlr comp_ref, UINT32 ete_id)
261runs on DIAMETER_Emulation_CT {
262 var integer i;
263 for (i := 0; i < sizeof(DiameterETEIDTable); i := i+1) {
264 if (DiameterETEIDTable[i].comp_ref == comp_ref and
265 DiameterETEIDTable[i].ete_id == ete_id) {
266 DiameterETEIDTable[i].ete_id := omit;
267 DiameterETEIDTable[i].comp_ref := null;
268 return;
269 }
270 }
271 setverdict(fail, "DIAMETER ETEID Table: Couldn't find to-be-deleted entry!");
272 mtc.stop;
273}
274
275
276private function f_eteid_table_init()
277runs on DIAMETER_Emulation_CT {
278 for (var integer i := 0; i < sizeof(DiameterETEIDTable); i := i+1) {
279 DiameterETEIDTable[i].comp_ref := null;
280 DiameterETEIDTable[i].ete_id := omit;
281 }
282}
283
Harald Welted27ab242019-07-26 13:45:18 +0200284function f_DIAMETER_get_imsi(PDU_DIAMETER pdu) return template (omit) IMSI
285{
286 var template (omit) AVP imsi_avp;
287
288 imsi_avp := f_DIAMETER_get_avp(pdu, c_AVP_Code_BASE_NONE_User_Name);
289 if (istemplatekind(imsi_avp, "omit")) {
Harald Weltef9fb63e2020-04-26 18:07:19 +0200290 var template (omit) AVP sid_avp;
291 sid_avp := f_DIAMETER_get_avp(pdu, c_AVP_Code_DCC_NONE_Subscription_Id);
292 if (istemplatekind(sid_avp, "omit")) {
293 return omit;
294 }
295 var AVP_Grouped grp := valueof(sid_avp.avp_data.avp_DCC_NONE_Subscription_Id);
Pau Espin Pedrolb8cd34a2022-05-18 16:34:12 +0200296 if (not match(grp[0], tr_AVP_SubcrIdType(END_USER_IMSI))) {
Harald Weltef9fb63e2020-04-26 18:07:19 +0200297 return omit;
298 }
299 return str2hex(oct2char(grp[1].avp.avp_data.avp_DCC_NONE_Subscription_Id_Data));
Harald Welted27ab242019-07-26 13:45:18 +0200300 } else {
301 var octetstring imsi_oct := valueof(imsi_avp.avp_data.avp_BASE_NONE_User_Name);
Pau Espin Pedrol518e24b2024-02-23 17:45:08 +0100302 var charstring imsi_str := oct2char(imsi_oct);
303 /* Username may be a NAI instead of IMSI: "<IMSI>@nai.epc.mnc<MNC>.mcc<MCC>.3gppnetwork.org" */
304 var integer pos := f_strstr(imsi_str, "@");
305 if (pos != -1) {
306 imsi_str := substr(imsi_str, 0, pos);
307 }
308 return str2hex(imsi_str);
Harald Welted27ab242019-07-26 13:45:18 +0200309 }
310}
311
Harald Welted27ab242019-07-26 13:45:18 +0200312private function f_diameter_xceive(template (value) PDU_DIAMETER tx,
313 template PDU_DIAMETER rx_t := ?)
314runs on DIAMETER_Emulation_CT return PDU_DIAMETER {
315 timer T := 10.0;
316 var DIAMETER_RecvFrom mrf;
317
318 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, tx));
Vadim Yanitskiy672a41e2023-02-14 04:52:56 +0700319 T.start;
Harald Welted27ab242019-07-26 13:45:18 +0200320 alt {
321 [] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(rx_t)) -> value mrf { }
Vadim Yanitskiy3148a0e2023-02-11 09:27:42 +0700322 [] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(?)) -> value mrf {
323 setverdict(fail, "Rx unexpected DIAMETER PDU: ", mrf);
324 mtc.stop;
325 }
Harald Welted27ab242019-07-26 13:45:18 +0200326 [] DIAMETER.receive(tr_SctpAssocChange) { repeat; }
Vadim Yanitskiy741aa572024-06-12 06:16:43 +0700327 [] DIAMETER.receive(tr_SctpPeerAddrChange) { repeat; }
Harald Welted27ab242019-07-26 13:45:18 +0200328 [] T.timeout {
329 setverdict(fail, "Timeout waiting for ", rx_t);
330 mtc.stop;
331 }
332 }
333 return mrf.msg;
334}
335
336function main(DIAMETEROps ops, DIAMETER_conn_parameters p, charstring id) runs on DIAMETER_Emulation_CT {
Vadim Yanitskiyb94e4592023-02-11 08:09:09 +0700337 var boolean server_mode := p.remote_sctp_port == -1;
Harald Welted27ab242019-07-26 13:45:18 +0200338 var Result res;
339 g_diameter_id := id;
340 f_imsi_table_init();
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200341 f_eteid_table_init();
Harald Welted27ab242019-07-26 13:45:18 +0200342 f_expect_table_init();
343
344 map(self:DIAMETER, system:DIAMETER_CODEC_PT);
Vadim Yanitskiyb94e4592023-02-11 08:09:09 +0700345 if (server_mode) {
Vadim Yanitskiy741aa572024-06-12 06:16:43 +0700346 res := DIAMETER_CodecPort_CtrlFunct.f_IPL4_listen(DIAMETER, p.local_ip, p.local_sctp_port,
347 { sctp := valueof(ts_SctpTuple) });
Harald Welted27ab242019-07-26 13:45:18 +0200348 } else {
349 res := DIAMETER_CodecPort_CtrlFunct.f_IPL4_connect(DIAMETER, p.remote_ip, p.remote_sctp_port,
Vadim Yanitskiy741aa572024-06-12 06:16:43 +0700350 p.local_ip, p.local_sctp_port, -1,
351 { sctp := valueof(ts_SctpTuple) });
Harald Welted27ab242019-07-26 13:45:18 +0200352 }
353 if (not ispresent(res.connId)) {
354 setverdict(fail, "Could not connect DIAMETER socket, check your configuration");
355 mtc.stop;
356 }
357 g_diameter_conn_id := res.connId;
358
Vadim Yanitskiyb94e4592023-02-11 08:09:09 +0700359 /* If in client mode, send CER immediately */
360 if (not server_mode) {
361 var template (value) PDU_DIAMETER req;
362 var PDU_DIAMETER rsp;
363
364 req := ts_DIA_CER(f_inet_addr(p.local_ip), p.vendor_app_id,
365 orig_host := p.origin_host, orig_realm := p.origin_realm);
366 rsp := f_diameter_xceive(req, tr_DIAMETER_A(Capabilities_Exchange, req.application_id));
367 /* notify our user that the CER->CEA exchange has happened */
368 DIAMETER_UNIT.send(DiameterCapabilityExchgInd:{rx := rsp, tx := valueof(req)});
369 }
370
Harald Welted27ab242019-07-26 13:45:18 +0200371 while (true) {
372 var DIAMETER_ConnHdlr vc_conn;
Harald Welted27ab242019-07-26 13:45:18 +0200373 var template IMSI imsi_t;
374 var hexstring imsi;
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200375 var UINT32 ete_id;
Harald Welted27ab242019-07-26 13:45:18 +0200376 var DIAMETER_RecvFrom mrf;
377 var PDU_DIAMETER msg;
378 var charstring vlr_name, mme_name;
379 var PortEvent port_evt;
380
381 alt {
382 [] DIAMETER.receive(PortEvent:{connOpened := ?}) -> value port_evt {
383 g_diameter_conn_id := port_evt.connOpened.connId;
384 }
385 [] DIAMETER.receive(PortEvent:?) { }
386 /* DIAMETER from client */
387 [] DIAMETER_CLIENT.receive(PDU_DIAMETER:?) -> value msg sender vc_conn {
388 /* Pass message through */
389 /* TODO: check which ConnectionID client has allocated + store in table? */
390 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, msg));
391 }
392
393 /* handle CER/CEA handshake */
394 [] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(tr_DIAMETER_R(cmd_code := Capabilities_Exchange))) -> value mrf {
395 var template (value) PDU_DIAMETER resp;
Pau Espin Pedrol33b47492022-03-08 17:43:01 +0100396 resp := f_ts_DIA_CEA(mrf.msg.hop_by_hop_id, mrf.msg.end_to_end_id, p.origin_host,
397 p.origin_realm, f_inet_addr(p.local_ip), p.auth_app_id, p.vendor_app_id);
Harald Welted27ab242019-07-26 13:45:18 +0200398 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, resp));
Harald Welted01b5d02020-04-26 22:05:53 +0200399 /* notify our user that the CER->CEA exchange has happened */
400 DIAMETER_UNIT.send(DiameterCapabilityExchgInd:{rx:=mrf.msg, tx:=valueof(resp)});
Harald Welted27ab242019-07-26 13:45:18 +0200401 }
Vadim Yanitskiy3f7a6dc2021-12-11 04:13:59 +0300402 /* handle DWR/DWA ping-pong */
403 [] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(tr_DIA_DWR)) -> value mrf {
404 var template (value) PDU_DIAMETER resp;
405 resp := ts_DIA_DWA('00000001'O, p.origin_host, p.origin_realm,
406 hbh_id := mrf.msg.hop_by_hop_id,
407 ete_id := mrf.msg.end_to_end_id);
408 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, valueof(resp)));
409 }
Harald Welted27ab242019-07-26 13:45:18 +0200410
Vadim Yanitskiyb46f01e2021-12-06 03:23:13 +0300411 /* DIAMETER from the test suite */
412 [ops.raw] DIAMETER_UNIT.receive(PDU_DIAMETER:?) -> value msg {
413 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, msg));
414 }
415 /* DIAMETER from remote peer (raw mode) */
416 [ops.raw] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(?)) -> value mrf {
417 DIAMETER_UNIT.send(mrf.msg);
418 }
419 /* DIAMETER from remote peer (IMSI based routing) */
420 [not ops.raw] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(?)) -> value mrf {
Harald Welted27ab242019-07-26 13:45:18 +0200421 imsi_t := f_DIAMETER_get_imsi(mrf.msg);
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200422 ete_id := mrf.msg.end_to_end_id;
423 if (f_ete_id_known(ete_id)) {
424 vc_conn := f_comp_by_ete_id(ete_id);
425 /* The ete_id is a single-time expect: */
426 f_eteid_table_del(vc_conn, ete_id);
427 DIAMETER_CLIENT.send(mrf.msg) to vc_conn;
428 } else if (isvalue(imsi_t)) {
Harald Welted27ab242019-07-26 13:45:18 +0200429 imsi := valueof(imsi_t);
430 if (f_imsi_known(imsi)) {
431 vc_conn := f_comp_by_imsi(imsi);
432 DIAMETER_CLIENT.send(mrf.msg) to vc_conn;
433 } else {
434 vc_conn := ops.create_cb.apply(mrf.msg, imsi, id);
435 f_imsi_table_add(vc_conn, imsi);
436 DIAMETER_CLIENT.send(mrf.msg) to vc_conn;
437 }
438 } else {
439 /* message contained no IMSI; is not IMSI-oriented */
440 var template PDU_DIAMETER resp := ops.unitdata_cb.apply(mrf.msg);
441 if (isvalue(resp)) {
442 DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, valueof(resp)));
443 }
444 }
445 }
446 [] DIAMETER.receive(tr_SctpAssocChange) { }
Vadim Yanitskiy741aa572024-06-12 06:16:43 +0700447 [] DIAMETER.receive(tr_SctpPeerAddrChange) { }
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200448 [] DIAMETER_PROC.getcall(DIAMETEREM_register_imsi:{?,?}) -> param(imsi, vc_conn) {
Harald Welted27ab242019-07-26 13:45:18 +0200449 f_create_expect(imsi, vc_conn);
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200450 DIAMETER_PROC.reply(DIAMETEREM_register_imsi:{imsi, vc_conn}) to vc_conn;
451 }
452 [] DIAMETER_PROC.getcall(DIAMETEREM_register_eteid:{?,?}) -> param(ete_id, vc_conn) {
453 f_eteid_table_add(vc_conn, ete_id);
454 DIAMETER_PROC.reply(DIAMETEREM_register_eteid:{ete_id, vc_conn}) to vc_conn;
Harald Welted27ab242019-07-26 13:45:18 +0200455 }
456
457 }
458
459 }
460}
461
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200462/* "E2E ID Expect" Handling */
463type record ExpectDataE2EID {
464 UINT32 ete_id optional,
465 DIAMETER_ConnHdlr vc_conn
466}
467
468signature DIAMETEREM_register_eteid(in UINT32 ete_id, in DIAMETER_ConnHdlr hdlr);
469
470/* client/conn_hdlr side function to use procedure port to create expect in emulation */
471function f_diameter_expect_eteid(UINT32 ete_id) runs on DIAMETER_ConnHdlr {
472 DIAMETER_PROC.call(DIAMETEREM_register_eteid:{ete_id, self}) {
473 [] DIAMETER_PROC.getreply(DIAMETEREM_register_eteid:{?,?}) {};
474 }
475}
476
477/* "IMSI Expect" Handling */
Harald Welted27ab242019-07-26 13:45:18 +0200478
479type record ExpectData {
480 hexstring imsi optional,
481 DIAMETER_ConnHdlr vc_conn
482}
483
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200484signature DIAMETEREM_register_imsi(in hexstring imsi, in DIAMETER_ConnHdlr hdlr);
Harald Welted27ab242019-07-26 13:45:18 +0200485
Pau Espin Pedrol69730a22022-04-11 12:00:53 +0200486/* Function that can be used as create_cb and will use the expect table */
Harald Welted27ab242019-07-26 13:45:18 +0200487function ExpectedCreateCallback(PDU_DIAMETER msg, hexstring imsi, charstring id)
488runs on DIAMETER_Emulation_CT return DIAMETER_ConnHdlr {
489 var DIAMETER_ConnHdlr ret := null;
490 var integer i;
491
492 for (i := 0; i < sizeof(DiameterExpectTable); i := i+1) {
493 if (not ispresent(DiameterExpectTable[i].imsi)) {
494 continue;
495 }
496 if (imsi == DiameterExpectTable[i].imsi) {
497 ret := DiameterExpectTable[i].vc_conn;
498 /* Release this entry */
499 DiameterExpectTable[i].imsi := omit;
500 DiameterExpectTable[i].vc_conn := null;
501 log("Found Expect[", i, "] for ", msg, " handled at ", ret);
502 return ret;
503 }
504 }
505 setverdict(fail, "Couldn't find Expect for ", msg);
506 mtc.stop;
507}
508
509private function f_create_expect(hexstring imsi, DIAMETER_ConnHdlr hdlr)
510runs on DIAMETER_Emulation_CT {
511 var integer i;
512
513 /* Check an entry like this is not already presnt */
514 for (i := 0; i < sizeof(DiameterExpectTable); i := i+1) {
515 if (imsi == DiameterExpectTable[i].imsi) {
516 setverdict(fail, "IMSI already present", imsi);
517 mtc.stop;
518 }
519 }
520 for (i := 0; i < sizeof(DiameterExpectTable); i := i+1) {
521 if (not ispresent(DiameterExpectTable[i].imsi)) {
522 DiameterExpectTable[i].imsi := imsi;
523 DiameterExpectTable[i].vc_conn := hdlr;
524 log("Created Expect[", i, "] for ", imsi, " to be handled at ", hdlr);
525 return;
526 }
527 }
528 testcase.stop("No space left in DiameterExpectTable")
529}
530
531/* client/conn_hdlr side function to use procedure port to create expect in emulation */
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200532function f_diameter_expect_imsi(hexstring imsi) runs on DIAMETER_ConnHdlr {
533 DIAMETER_PROC.call(DIAMETEREM_register_imsi:{imsi, self}) {
534 [] DIAMETER_PROC.getreply(DIAMETEREM_register_imsi:{?,?}) {};
Harald Welted27ab242019-07-26 13:45:18 +0200535 }
536}
537
538private function f_expect_table_init()
539runs on DIAMETER_Emulation_CT {
540 var integer i;
541 for (i := 0; i < sizeof(DiameterExpectTable); i := i + 1) {
542 DiameterExpectTable[i].imsi := omit;
543 }
544}
545
546function DummyUnitdataCallback(PDU_DIAMETER msg)
547runs on DIAMETER_Emulation_CT return template PDU_DIAMETER {
548 log("Ignoring DIAMETER ", msg);
549 return omit;
550}
551
Pau Espin Pedroldb017f42023-08-25 19:22:25 +0200552type port DIAMETEREM_PROC_PT procedure {
553 inout DIAMETEREM_register_imsi;
554 inout DIAMETEREM_register_eteid;
555} with { extension "internal" };
Harald Welted27ab242019-07-26 13:45:18 +0200556
Harald Welted01b5d02020-04-26 22:05:53 +0200557function f_diameter_wait_capability(DIAMETER_PT pt)
558{
559 /* Wait for the Capability Exchange with the DUT */
560 timer T := 10.0;
561 T.start;
562 alt {
563 [] pt.receive(DiameterCapabilityExchgInd:?) {}
564 [] pt.receive {
565 setverdict(fail, "Unexpected receive waiting for DiameterCapabilityExchgInd");
566 mtc.stop;
567 }
568 [] T.timeout {
569 setverdict(fail, "Timeout waiting for DiameterCapabilityExchgInd");
570 mtc.stop;
571 }
572 }
573}
574
575
Harald Welted27ab242019-07-26 13:45:18 +0200576}