blob: d5a15a2aec49c3cf95cea549b2aca0aa80fdda15 [file] [log] [blame]
Neels Hofmeyra2d1d7a2022-01-20 23:11:38 +01001/* PFCP Emulation in TTCN-3
2 *
3 * (C) 2022 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
12module PFCP_Emulation {
13
14import from IPL4asp_Types all;
15import from General_Types all;
16import from Osmocom_Types all;
17import from PFCP_Types all;
18import from PFCP_CodecPort all;
19import from PFCP_CodecPort_CtrlFunct all;
20
21/***********************************************************************
22 * Main Emulation Component
23 ***********************************************************************/
24
25const integer PFCP_PORT := 8805;
26
27type enumerated PFCP_Role {
28 CPF,
29 UPF
30};
31
32type record PFCP_Emulation_Cfg {
33 HostName pfcp_bind_ip,
34 PortNumber pfcp_bind_port,
35 HostName pfcp_remote_ip,
36 PortNumber pfcp_remote_port,
37 PFCP_Role role
38};
39
40type component PFCP_Emulation_CT {
41 /* Communication with underlying PFCP CodecPort */
42 port PFCP_PT PFCP;
43
44 /* Communication with Clients */
45 port PFCPEM_PT CLIENT;
46 port PFCPEM_PROC_PT CLIENT_PROC;
47
48 /* Configuration by the user */
49 var PFCP_Emulation_Cfg g_pfcp_cfg;
50
51 /* State */
52 var integer g_pfcp_conn_id;
53 var integer g_recovery_timestamp;
54
55 var PFCPEM_conns g_conns;
56
57 var integer g_next_sequence_nr_state;
58};
59
60private function f_PFCPEM_next_sequence_nr() runs on PFCP_Emulation_CT return integer {
61 g_next_sequence_nr_state := g_next_sequence_nr_state + 1;
62 if (g_next_sequence_nr_state > 16777215) {
63 g_next_sequence_nr_state := 1;
64 }
65 return g_next_sequence_nr_state;
66}
67
68type record PFCPEM_conn {
69 PFCP_ConnHdlr vc_conn,
70 OCT8 seid optional,
71 LIN3_BO_LAST pfcp_msg_sequence_number optional
72};
73
74type record of PFCPEM_conn PFCPEM_conns;
75
76private function f_PFCPEM_conn_by_seid_or_seqnr(OCT8 seid, LIN3_BO_LAST seqnr) runs on PFCP_Emulation_CT return PFCP_ConnHdlr {
77 log("looking for seid ", seid, " seqnr ", seqnr, " in conns ", g_conns);
78 for (var integer i := 0; i < lengthof(g_conns); i := i + 1) {
79 if (isbound(g_conns[i].pfcp_msg_sequence_number)
80 and seqnr == g_conns[i].pfcp_msg_sequence_number) {
81 return g_conns[i].vc_conn;
82 }
83 if (isbound(g_conns[i].seid)
84 and seid == g_conns[i].seid) {
85 return g_conns[i].vc_conn;
86 }
87 }
88 return null;
89};
90
91private function f_PFCPEM_add_conn(PFCP_ConnHdlr vc_conn) runs on PFCP_Emulation_CT {
92 for (var integer i := 0; i < lengthof(g_conns); i := i + 1) {
93 if (g_conns[i].vc_conn == vc_conn) {
94 return;
95 }
96 }
97 /* Not in the list yet, add. */
98 var PFCPEM_conn conn := { vc_conn := vc_conn };
99 g_conns := g_conns & { conn };
100}
101
102private function f_init(PFCP_Emulation_Cfg cfg) runs on PFCP_Emulation_CT {
103 var Result res;
104
105 map(self:PFCP, system:PFCP);
106 res := PFCP_CodecPort_CtrlFunct.f_IPL4_listen(PFCP, cfg.pfcp_bind_ip, cfg.pfcp_bind_port, {udp:={}});
107 g_pfcp_conn_id := res.connId;
108
109 g_recovery_timestamp := f_rnd_int(4294967296);
110 g_pfcp_cfg := cfg;
111
112 g_conns := {};
113
114 g_next_sequence_nr_state := (1 + f_rnd_int(1000)) * 10000;
115}
116
117function main(PFCP_Emulation_Cfg cfg) runs on PFCP_Emulation_CT {
118 var PFCP_ConnHdlr vc_conn;
119 var PFCP_Unitdata ud;
120 var PDU_PFCP pdu;
121
122 f_init(cfg);
123
124 while (true) {
125 alt {
126 [] PFCP.receive(PFCP_Unitdata:?) -> value ud {
127 log("PFCP_Emulation main() PFCP.receive: ", ud);
128 vc_conn := null;
129 if (ud.pdu.s_flag == '1'B) {
130 /* There is a SEID */
131 vc_conn := f_PFCPEM_conn_by_seid_or_seqnr(ud.pdu.seid, ud.pdu.sequence_number);
132 }
133 if (vc_conn != null) {
134 log("found destination ", vc_conn);
135 CLIENT.send(ud.pdu) to vc_conn;
136 } else {
137 log("sending to all conns: ", g_conns);
138 for (var integer i := 0; i < lengthof(g_conns); i := i + 1) {
139 CLIENT.send(ud.pdu) to g_conns[i].vc_conn;
140 }
141 }
142 }
143
144 [] CLIENT.receive(PDU_PFCP:?) -> value pdu sender vc_conn {
145 log("PFCP_Emulation main() CLIENT.receive from ", vc_conn, ": ", pdu);
146 if (pdu.sequence_number == 0) {
147 pdu.sequence_number := f_PFCPEM_next_sequence_nr();
148 }
149 ud := {
150 peer := {
151 conn_id := g_pfcp_conn_id,
152 remote_name := g_pfcp_cfg.pfcp_remote_ip,
153 remote_port := g_pfcp_cfg.pfcp_remote_port
154 },
155 pdu := pdu
156 };
157
158 f_PFCPEM_add_conn(vc_conn);
159
160 PFCP.send(ud);
161 }
162
163 [] CLIENT_PROC.getcall(PFCPEM_register:{}) -> sender vc_conn {
164 log("PFCP_Emulation main() CLIENT_PROC.getcall(PFCPEM_register)");
165 f_PFCPEM_add_conn(vc_conn);
166 CLIENT_PROC.reply(PFCPEM_register:{}) to vc_conn;
167 }
168 }
169 }
170}
171
172
173/***********************************************************************
174 * Interaction between Main and Client Components
175 ***********************************************************************/
176type port PFCPEM_PT message {
177 inout PDU_PFCP;
178} with { extension "internal" };
179
180signature PFCPEM_register();
181
182type port PFCPEM_PROC_PT procedure {
183 inout PFCPEM_register;
184} with { extension "internal" };
185
186/***********************************************************************
Philipp Maier52290592024-03-11 14:49:01 +0100187 * Client Component
Neels Hofmeyra2d1d7a2022-01-20 23:11:38 +0100188 ***********************************************************************/
189
190type component PFCP_ConnHdlr {
191 port PFCPEM_PT PFCP;
192 port PFCPEM_PROC_PT PFCP_PROC;
193 var PFCP_Emulation_CT vc_PFCP;
194};
195
196function f_pfcp_register() runs on PFCP_ConnHdlr {
197 PFCP_PROC.call(PFCPEM_register:{}) {
198 [] PFCP_PROC.getreply(PFCPEM_register:{});
199 }
200}
201
202}