aboutsummaryrefslogtreecommitdiffstats
path: root/library/PFCP_Emulation.ttcn
blob: d5a15a2aec49c3cf95cea549b2aca0aa80fdda15 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* 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 Component
 ***********************************************************************/

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

}