| /* 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:{}); |
| } |
| } |
| |
| } |