203 lines
5.2 KiB
Plaintext
203 lines
5.2 KiB
Plaintext
/* PFCP Emulation in TTCN-3
|
|
*
|
|
* (C) 2022 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
|
* All rights reserved.
|
|
*
|
|
* Released under the terms of GNU General Public License, Version 2 or
|
|
* (at your option) any later version.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
module PFCP_Emulation {
|
|
|
|
import from IPL4asp_Types all;
|
|
import from General_Types all;
|
|
import from Osmocom_Types all;
|
|
import from PFCP_Types all;
|
|
import from PFCP_CodecPort all;
|
|
import from PFCP_CodecPort_CtrlFunct all;
|
|
|
|
/***********************************************************************
|
|
* Main Emulation Component
|
|
***********************************************************************/
|
|
|
|
const integer PFCP_PORT := 8805;
|
|
|
|
type enumerated PFCP_Role {
|
|
CPF,
|
|
UPF
|
|
};
|
|
|
|
type record PFCP_Emulation_Cfg {
|
|
HostName pfcp_bind_ip,
|
|
PortNumber pfcp_bind_port,
|
|
HostName pfcp_remote_ip,
|
|
PortNumber pfcp_remote_port,
|
|
PFCP_Role role
|
|
};
|
|
|
|
type component PFCP_Emulation_CT {
|
|
/* Communication with underlying PFCP CodecPort */
|
|
port PFCP_PT PFCP;
|
|
|
|
/* Communication with Clients */
|
|
port PFCPEM_PT CLIENT;
|
|
port PFCPEM_PROC_PT CLIENT_PROC;
|
|
|
|
/* Configuration by the user */
|
|
var PFCP_Emulation_Cfg g_pfcp_cfg;
|
|
|
|
/* State */
|
|
var integer g_pfcp_conn_id;
|
|
var integer g_recovery_timestamp;
|
|
|
|
var PFCPEM_conns g_conns;
|
|
|
|
var integer g_next_sequence_nr_state;
|
|
};
|
|
|
|
private function f_PFCPEM_next_sequence_nr() runs on PFCP_Emulation_CT return integer {
|
|
g_next_sequence_nr_state := g_next_sequence_nr_state + 1;
|
|
if (g_next_sequence_nr_state > 16777215) {
|
|
g_next_sequence_nr_state := 1;
|
|
}
|
|
return g_next_sequence_nr_state;
|
|
}
|
|
|
|
type record PFCPEM_conn {
|
|
PFCP_ConnHdlr vc_conn,
|
|
OCT8 seid optional,
|
|
LIN3_BO_LAST pfcp_msg_sequence_number optional
|
|
};
|
|
|
|
type record of PFCPEM_conn PFCPEM_conns;
|
|
|
|
private function f_PFCPEM_conn_by_seid_or_seqnr(OCT8 seid, LIN3_BO_LAST seqnr) runs on PFCP_Emulation_CT return PFCP_ConnHdlr {
|
|
log("looking for seid ", seid, " seqnr ", seqnr, " in conns ", g_conns);
|
|
for (var integer i := 0; i < lengthof(g_conns); i := i + 1) {
|
|
if (isbound(g_conns[i].pfcp_msg_sequence_number)
|
|
and seqnr == g_conns[i].pfcp_msg_sequence_number) {
|
|
return g_conns[i].vc_conn;
|
|
}
|
|
if (isbound(g_conns[i].seid)
|
|
and seid == g_conns[i].seid) {
|
|
return g_conns[i].vc_conn;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
private function f_PFCPEM_add_conn(PFCP_ConnHdlr vc_conn) runs on PFCP_Emulation_CT {
|
|
for (var integer i := 0; i < lengthof(g_conns); i := i + 1) {
|
|
if (g_conns[i].vc_conn == vc_conn) {
|
|
return;
|
|
}
|
|
}
|
|
/* Not in the list yet, add. */
|
|
var PFCPEM_conn conn := { vc_conn := vc_conn };
|
|
g_conns := g_conns & { conn };
|
|
}
|
|
|
|
private function f_init(PFCP_Emulation_Cfg cfg) runs on PFCP_Emulation_CT {
|
|
var Result res;
|
|
|
|
map(self:PFCP, system:PFCP);
|
|
res := PFCP_CodecPort_CtrlFunct.f_IPL4_listen(PFCP, cfg.pfcp_bind_ip, cfg.pfcp_bind_port, {udp:={}});
|
|
g_pfcp_conn_id := res.connId;
|
|
|
|
g_recovery_timestamp := f_rnd_int(4294967296);
|
|
g_pfcp_cfg := cfg;
|
|
|
|
g_conns := {};
|
|
|
|
g_next_sequence_nr_state := (1 + f_rnd_int(1000)) * 10000;
|
|
}
|
|
|
|
function main(PFCP_Emulation_Cfg cfg) runs on PFCP_Emulation_CT {
|
|
var PFCP_ConnHdlr vc_conn;
|
|
var PFCP_Unitdata ud;
|
|
var PDU_PFCP pdu;
|
|
|
|
f_init(cfg);
|
|
|
|
while (true) {
|
|
alt {
|
|
[] PFCP.receive(PFCP_Unitdata:?) -> value ud {
|
|
log("PFCP_Emulation main() PFCP.receive: ", ud);
|
|
vc_conn := null;
|
|
if (ud.pdu.s_flag == '1'B) {
|
|
/* There is a SEID */
|
|
vc_conn := f_PFCPEM_conn_by_seid_or_seqnr(ud.pdu.seid, ud.pdu.sequence_number);
|
|
}
|
|
if (vc_conn != null) {
|
|
log("found destination ", vc_conn);
|
|
CLIENT.send(ud.pdu) to vc_conn;
|
|
} else {
|
|
log("sending to all conns: ", g_conns);
|
|
for (var integer i := 0; i < lengthof(g_conns); i := i + 1) {
|
|
CLIENT.send(ud.pdu) to g_conns[i].vc_conn;
|
|
}
|
|
}
|
|
}
|
|
|
|
[] CLIENT.receive(PDU_PFCP:?) -> value pdu sender vc_conn {
|
|
log("PFCP_Emulation main() CLIENT.receive from ", vc_conn, ": ", pdu);
|
|
if (pdu.sequence_number == 0) {
|
|
pdu.sequence_number := f_PFCPEM_next_sequence_nr();
|
|
}
|
|
ud := {
|
|
peer := {
|
|
conn_id := g_pfcp_conn_id,
|
|
remote_name := g_pfcp_cfg.pfcp_remote_ip,
|
|
remote_port := g_pfcp_cfg.pfcp_remote_port
|
|
},
|
|
pdu := pdu
|
|
};
|
|
|
|
f_PFCPEM_add_conn(vc_conn);
|
|
|
|
PFCP.send(ud);
|
|
}
|
|
|
|
[] CLIENT_PROC.getcall(PFCPEM_register:{}) -> sender vc_conn {
|
|
log("PFCP_Emulation main() CLIENT_PROC.getcall(PFCPEM_register)");
|
|
f_PFCPEM_add_conn(vc_conn);
|
|
CLIENT_PROC.reply(PFCPEM_register:{}) to vc_conn;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* Interaction between Main and Client Components
|
|
***********************************************************************/
|
|
type port PFCPEM_PT message {
|
|
inout PDU_PFCP;
|
|
} with { extension "internal" };
|
|
|
|
signature PFCPEM_register();
|
|
|
|
type port PFCPEM_PROC_PT procedure {
|
|
inout PFCPEM_register;
|
|
} with { extension "internal" };
|
|
|
|
/***********************************************************************
|
|
* Client Compoennt
|
|
***********************************************************************/
|
|
|
|
type component PFCP_ConnHdlr {
|
|
port PFCPEM_PT PFCP;
|
|
port PFCPEM_PROC_PT PFCP_PROC;
|
|
var PFCP_Emulation_CT vc_PFCP;
|
|
};
|
|
|
|
function f_pfcp_register() runs on PFCP_ConnHdlr {
|
|
PFCP_PROC.call(PFCPEM_register:{}) {
|
|
[] PFCP_PROC.getreply(PFCPEM_register:{});
|
|
}
|
|
}
|
|
|
|
}
|