blob: cb8c48966a760e37a823a7d3e1f9ad00393c7e12 [file] [log] [blame]
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001module UPF_Tests {
2
3/* Integration Tests for OsmoUPF
4 * (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
5 * All rights reserved.
6 *
7 * Released under the terms of GNU General Public License, Version 2 or
8 * (at your option) any later version.
9 *
10 * SPDX-License-Identifier: GPL-2.0-or-later
11 *
12 * This test suite acts as a PFCP Control Plane Function to test OsmoUPF.
13 */
14
15import from Misc_Helpers all;
16import from General_Types all;
17import from Osmocom_Types all;
18import from IPL4asp_Types all;
19import from Native_Functions all;
20import from TCCConversion_Functions all;
21
22import from Osmocom_CTRL_Functions all;
23import from Osmocom_CTRL_Types all;
24import from Osmocom_CTRL_Adapter all;
25
26import from StatsD_Types all;
27import from StatsD_CodecPort all;
28import from StatsD_CodecPort_CtrlFunct all;
29import from StatsD_Checker all;
30
31import from Osmocom_VTY_Functions all;
32import from TELNETasp_PortType all;
33
34import from CPF_ConnectionHandler all;
35
36import from PFCP_Types all;
37import from PFCP_Emulation all;
38import from PFCP_Templates all;
39
40modulepar {
41 /* IP address at which the UPF can be reached */
42 charstring mp_pfcp_ip_upf := "127.0.0.1";
43 charstring mp_pfcp_ip_local := "127.0.0.2";
44
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +010045 charstring mp_netinst_access_ip_1 := "127.0.1.1";
46 charstring mp_netinst_access_ip_2 := "127.0.1.2";
47 charstring mp_netinst_core_ip_1 := "127.0.2.1";
48 charstring mp_netinst_core_ip_2 := "127.0.2.2";
49
Neels Hofmeyr2d292742022-06-07 23:58:05 +020050 /* When testing with gtp mockup, actions will not show. */
51 boolean mp_verify_gtp_actions := false;
52}
53
54type component test_CT extends CTRL_Adapter_CT {
55 port PFCPEM_PT PFCP;
56
57 port TELNETasp_PT UPFVTY;
58
59 /* global test case guard timer (actual timeout value is set in f_init()) */
60 timer T_guard := 15.0;
61}
62
63/* global altstep for global guard timer; */
64altstep as_Tguard() runs on test_CT {
65 [] T_guard.timeout {
66 setverdict(fail, "Timeout of T_guard");
67 mtc.stop;
68 }
69}
70
Neels Hofmeyr2d292742022-06-07 23:58:05 +020071private function f_get_name_val(out charstring val, charstring str, charstring name, charstring sep := ":", charstring delim := " ") return boolean {
72 var charstring labl := name & sep;
73 var integer namepos := f_strstr(str, labl);
74 if (namepos < 0) {
75 return false;
76 }
77 var integer valpos := namepos + lengthof(labl);
78 var integer valend := f_strstr(str, delim, valpos);
79 if (valend < 0) {
80 valend := lengthof(str);
81 }
82 val := substr(str, valpos, valend - valpos);
83 return true;
84}
85
86private function f_get_name_val_oct8(out OCT8 val, charstring str, charstring name) return boolean {
87 var charstring token;
88 if (not f_get_name_val(token, str, name, ":0x")) {
89 return false;
90 }
91 if (lengthof(token) > 16) {
92 log("token too long: ", name, " in ", str);
93 return false;
94 }
95 var charstring padded := substr("0000000000000000", 0, 16 - lengthof(token)) & token;
96 val := str2oct(padded);
97 return true;
98}
99
100private function f_get_name_val_oct4(out OCT4 val, charstring str, charstring name) return boolean {
101 var charstring token;
102 if (not f_get_name_val(token, str, name, ":0x")) {
103 return false;
104 }
105 if (lengthof(token) > 8) {
106 log("token too long: ", name, " in ", str);
107 return false;
108 }
109 var charstring padded := substr("00000000", 0, 8 - lengthof(token)) & token;
110 val := str2oct(padded);
111 return true;
112}
113
114private function f_get_name_val_int(out integer val, charstring str, charstring name) return boolean {
115 var charstring token;
116 if (not f_get_name_val(token, str, name)) {
117 return false;
118 }
119 val := str2int(token);
120 return true;
121}
122
123private function f_get_name_val_2int(out integer val1, out integer val2, charstring str, charstring name, charstring delim := ",") return boolean {
124 var charstring token;
125 if (not f_get_name_val(token, str, name)) {
126 return false;
127 }
Pau Espin Pedrol601b5cd2024-04-17 19:16:17 +0200128 var Misc_Helpers.ro_charstring nrl := f_str_split(token, delim);
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200129 if (lengthof(nrl) != 2) {
130 return false;
131 }
132 val1 := str2int(nrl[0]);
133 val2 := str2int(nrl[1]);
134 return true;
135}
136
137/* A PFCP session as seen by the system under test, osmo-upf. up_seid is what osmo-upf sees as its local SEID
138 * ("SEID-l"). cp_seid is this tester's side's SEID, which osmo-upf sees as the remote SEID. */
139type record PFCP_session {
140 OCT8 up_seid,
141 OCT8 cp_seid,
142 GTP_Action gtp
143}
144
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100145/* _r and _l means 'remote' and 'local', from the perspective of the osmo-upf process. */
146type record GTP_Action_tunend {
147 /* the PDR Id detecting packets from this side */
148 integer pdr_id,
149 /* IP packets arriving from this side are arriving on ip_l */
150 charstring ip_l,
151
152 /* the FAR Id forwarding packets to this side */
153 integer far_id
154}
155
156/* _r and _l means 'remote' and 'local', from the perspective of the osmo-upf process. */
157type record GTP_Action_tun {
158 /* the PDR Id detecting packets from this side */
159 integer pdr_id,
160 /* GTP arriving from this side is arriving on gtp_ip_l with teid_l */
161 charstring gtp_ip_l,
162 OCT4 teid_l,
163
164 /* the FAR Id forwarding packets to this side */
165 integer far_id,
166 /* GTP going out to this side should be sent to gtp_ip_r with teid_r */
167 charstring gtp_ip_r,
168 OCT4 teid_r
169}
170
171type union GTP_Action_core {
172 /* For kind = "tunend", the local IP that the UE has on 'the internet' */
173 GTP_Action_tunend tunend,
174 /* For kind = "tunmap", the second GTP tunnel */
175 GTP_Action_tun tunmap
176}
177
178/* State of what GTP functionality osmo-upf should put in place, after a PFCP request was ACKed by it.
179 * _r and _l means 'remote' and 'local', from the perspective of the osmo-upf process.
180 *
181 * tunend:
182 * Access UPF Core
183 * GTP-r:127.0.0.2,0x1 <-FAR-1-- | 192.168.0.1 <-PDR-1--
184 * --PDR-2-> GTP-l:127.0.0.1,0x2 | --FAR-2-> (IP destination is in each GTP payload)
185 *
186 * tunmap:
187 * Access UPF Core
188 * GTP-r:127.0.0.2,0x1 <-FAR-1-- | 127.0.0.1,0x1 <-PDR-1--
189 * --PDR-2-> GTP-l:127.0.0.1,0x2 | --FAR-2-> GTP-r:127.0.0.3,0x2
190 */
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200191type record GTP_Action {
Neels Hofmeyr366cdc72022-11-21 16:28:00 +0100192 /* kind = ("tunend"|"tunmap") */
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200193 charstring kind,
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100194 /* The access side GTP tunnel. (The 'Access' side is always GTP.) */
195 GTP_Action_tun access,
196 /* The core side GTP tunnel (tunmap) or local IP (tunend) */
197 GTP_Action_core core,
198 /* Reference to the PFCP Session that created this GTP action: PFCP session's F-SEID as seen from osmo-upf */
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200199 charstring pfcp_peer,
200 OCT8 seid_l
201};
202
203type record of GTP_Action GTP_Action_List;
204
205private function f_parse_gtp_action(out GTP_Action ret, charstring str) return boolean {
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100206 /* Parse a string like
207 * "GTP:tunend GTP-access-r:127.0.0.2 TEID-access-r:0x94f0001 TEID-access-l:0x1 IP-core-l:192.168.44.241 PFCP-peer:127.0.0.2 SEID-l:0x1 PDR:1,2"
208 */
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200209 var GTP_Action a;
210 if (not f_get_name_val(a.kind, str, "GTP")) {
211 return false;
212 }
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100213 if (not f_get_name_val(a.access.gtp_ip_r, str, "GTP-access-r")) {
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200214 return false;
215 }
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100216 if (not f_get_name_val_oct4(a.access.teid_r, str, "TEID-access-r")) {
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200217 return false;
218 }
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100219 if (not f_get_name_val(a.access.gtp_ip_l, str, "GTP-access-l")) {
220 return false;
221 }
222 if (not f_get_name_val_oct4(a.access.teid_l, str, "TEID-access-l")) {
223 return false;
224 }
225 if (not f_get_name_val_int(a.access.pdr_id, str, "PDR-access")) {
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200226 return false;
227 }
228 if (not f_get_name_val(a.pfcp_peer, str, "PFCP-peer")) {
229 return false;
230 }
231 if (not f_get_name_val_oct8(a.seid_l, str, "SEID-l")) {
232 return false;
233 }
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100234 if (a.kind == "tunend") {
235 if (not f_get_name_val_int(a.core.tunend.pdr_id, str, "PDR-core")) {
236 return false;
237 }
238 if (not f_get_name_val(a.core.tunend.ip_l, str, "IP-core-l")) {
239 return false;
240 }
241 /* in these tests, the PDR Id and its FAR Id are always the same: PDR for incoming on Access matches its
242 * FAR that forwards to Core. */
243 a.core.tunend.far_id := a.access.pdr_id;
244 a.access.far_id := a.core.tunend.pdr_id;
245 } else if (a.kind == "tunmap") {
246 if (not f_get_name_val(a.core.tunmap.gtp_ip_r, str, "GTP-core-r")) {
247 return false;
248 }
249 if (not f_get_name_val_oct4(a.core.tunmap.teid_r, str, "TEID-core-r")) {
250 return false;
251 }
252 if (not f_get_name_val(a.core.tunmap.gtp_ip_l, str, "GTP-core-l")) {
253 return false;
254 }
255 if (not f_get_name_val_oct4(a.core.tunmap.teid_l, str, "TEID-core-l")) {
256 return false;
257 }
258 if (not f_get_name_val_int(a.core.tunmap.pdr_id, str, "PDR-core")) {
259 return false;
260 }
261 /* in these tests, the PDR Id and its FAR Id are always the same: PDR for incoming on Access matches its
262 * FAR that forwards to Core. */
263 a.core.tunmap.far_id := a.access.pdr_id;
264 a.access.far_id := a.core.tunmap.pdr_id;
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200265 }
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100266
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200267 ret := a;
268 return true;
269}
270
271private function f_vty_get_gtp_actions(TELNETasp_PT vty_pt) return GTP_Action_List {
272 var charstring gtp_str := f_vty_transceive_ret(vty_pt, "show gtp");
Pau Espin Pedrol601b5cd2024-04-17 19:16:17 +0200273 var Misc_Helpers.ro_charstring lines := f_str_split(gtp_str, "\n");
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200274 var GTP_Action_List gtps := {};
275 for (var integer i := 0; i < lengthof(lines); i := i + 1) {
276 var charstring line := lines[i];
277 var GTP_Action a;
278 if (not f_parse_gtp_action(a, line)) {
279 continue;
280 }
281 gtps := gtps & { a };
282 }
283 log("GTP-actions: ", gtps);
284 return gtps;
285}
286
287private function f_find_gtp_action(GTP_Action_List actions, template GTP_Action find) return boolean {
288 for (var integer i := 0; i < lengthof(actions); i := i + 1) {
289 if (match(actions[i], find)) {
290 return true;
291 }
292 }
293 return false;
294}
295
296private function f_expect_gtp_action(GTP_Action_List actions, template GTP_Action expect) {
297 if (f_find_gtp_action(actions, expect)) {
298 log("VTY confirms: GTP action active: ", expect);
299 setverdict(pass);
300 return;
301 }
302 log("Expected to find ", expect, " in ", actions);
303 setverdict(fail, "on VTY, a GTP action failed to show as active");
304 mtc.stop;
305}
306
307private function f_expect_no_gtp_action(GTP_Action_List actions, template GTP_Action expect) {
308 if (f_find_gtp_action(actions, expect)) {
309 log("Expected to *not* find ", expect, " in ", actions);
310 setverdict(fail, "a GTP action failed to show as inactive");
311 mtc.stop;
312 }
313 log("VTY confirms: GTP action inactive: ", expect);
314 setverdict(pass);
315 return;
316}
317
318private function f_vty_expect_gtp_action(TELNETasp_PT vty_pt, template GTP_Action expect) {
319 if (not mp_verify_gtp_actions) {
320 /* In GTP mockup mode, GTP actions don't show on VTY. Cannot verify. */
321 setverdict(pass);
322 return;
323 }
324 var GTP_Action_List actions := f_vty_get_gtp_actions(vty_pt);
325 f_expect_gtp_action(actions, expect);
326}
327
328private function f_vty_expect_no_gtp_actions(TELNETasp_PT vty_pt) {
329 var GTP_Action_List actions := f_vty_get_gtp_actions(vty_pt);
330 if (lengthof(actions) > 0) {
331 setverdict(fail, "VTY says that there are still active GTP actions");
332 mtc.stop;
333 }
334 setverdict(pass);
335}
336
337type record PFCP_Session_Status {
338 charstring peer,
339 OCT8 seid_r,
340 OCT8 seid_l,
341 charstring state,
342 integer pdr_active_count,
343 integer pdr_count,
344 integer far_active_count,
345 integer far_count,
346 integer gtp_active_count
347};
348
349template PFCP_Session_Status PFCP_session_active := {
350 peer := ?,
351 seid_r := ?,
352 seid_l := ?,
353 state := "ESTABLISHED",
354 pdr_active_count := (1..99999),
355 pdr_count := (1..99999),
356 far_active_count := (1..99999),
357 far_count := (1..99999),
358 gtp_active_count := (1..99999)
359};
360
361template PFCP_Session_Status PFCP_session_inactive := {
362 peer := ?,
363 seid_r := ?,
364 seid_l := ?,
365 state := "ESTABLISHED",
366 pdr_active_count := 0,
367 pdr_count := (1..99999),
368 far_active_count := 0,
369 far_count := (1..99999),
370 gtp_active_count := 0
371};
372
373type record of PFCP_Session_Status PFCP_Session_Status_List;
374
375private function f_parse_session_status(out PFCP_Session_Status ret, charstring str) return boolean {
376 var PFCP_Session_Status st;
377 if (not f_get_name_val(st.peer, str, "peer")) {
378 return false;
379 }
380 if (not f_get_name_val_oct8(st.seid_l, str, "SEID-l")) {
381 return false;
382 }
383 f_get_name_val_oct8(st.seid_r, str, "SEID-r");
384 f_get_name_val(st.state, str, "state");
385
386 /* parse 'PDR-active:1/2' */
387 if (not f_get_name_val_2int(st.pdr_active_count, st.pdr_count, str, "PDR-active", "/")) {
388 return false;
389 }
390 /* parse 'FAR-active:1/2' */
391 if (not f_get_name_val_2int(st.far_active_count, st.far_count, str, "FAR-active", "/")) {
392 return false;
393 }
394
395 f_get_name_val_int(st.gtp_active_count, str, "GTP-active");
396 ret := st;
397 return true;
398}
399
400private function f_vty_get_sessions(TELNETasp_PT vty_pt) return PFCP_Session_Status_List {
401 var charstring sessions_str := f_vty_transceive_ret(vty_pt, "show session");
Pau Espin Pedrol601b5cd2024-04-17 19:16:17 +0200402 var Misc_Helpers.ro_charstring lines := f_str_split(sessions_str, "\n");
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200403 var PFCP_Session_Status_List sessions := {};
404 for (var integer i := 0; i < lengthof(lines); i := i + 1) {
405 var charstring line := lines[i];
406 var PFCP_Session_Status st;
407 if (not f_parse_session_status(st, line)) {
408 continue;
409 }
410 sessions := sessions & { st };
411 }
412 log("Sessions: ", sessions);
413 return sessions;
414}
415
416private function f_vty_get_session_status(TELNETasp_PT vty_pt, PFCP_session s, out PFCP_Session_Status ret) return boolean {
417 var PFCP_Session_Status_List sessions := f_vty_get_sessions(vty_pt);
418 return f_get_session_status(sessions, s, ret);
419}
420
421private function f_get_session_status(PFCP_Session_Status_List sessions, PFCP_session s, out PFCP_Session_Status ret)
422return boolean {
423 var PFCP_Session_Status_List matches := {};
424 for (var integer i := 0; i < lengthof(sessions); i := i + 1) {
425 var PFCP_Session_Status st := sessions[i];
426 if (st.seid_l != s.up_seid) {
427 continue;
428 }
429 if (st.seid_r != s.cp_seid) {
430 continue;
431 }
432 matches := matches & { st };
433 }
434 if (lengthof(matches) < 1) {
435 log("no session with SEID-l = ", s.up_seid);
436 return false;
437 }
438 if (lengthof(matches) > 1) {
439 log("multiple sessions have ", s, ": ", matches);
440 return false;
441 }
442 ret := matches[0];
443 return true;
444}
445
446private function f_vty_expect_session_status(TELNETasp_PT vty_pt, PFCP_session s, template PFCP_Session_Status expect_st) {
447 var PFCP_Session_Status st;
448 if (not f_vty_get_session_status(vty_pt, s, st)) {
449 log("Session ", s, " not found in VTY session list");
450 setverdict(fail, "Session not found in VTY list");
451 mtc.stop;
452 }
453 log("Session ", s, " status: ", st);
454 if (not match(st, expect_st)) {
455 log("ERROR: Session ", st, " does not match ", expect_st);
456 setverdict(fail, "VTY shows unexpected state of PFCP session");
457 mtc.stop;
458 }
459
460 setverdict(pass);
461}
462
463private function f_vty_expect_session_active(TELNETasp_PT vty_pt, PFCP_session s)
464{
465 f_vty_expect_session_status(vty_pt, s, PFCP_session_active);
466 f_vty_expect_gtp_action(vty_pt, s.gtp);
467 setverdict(pass);
468}
469
470private function f_vty_expect_no_active_sessions(TELNETasp_PT vty_pt) {
471 var PFCP_Session_Status_List stl := f_vty_get_sessions(vty_pt);
472 var integer active := 0;
473 for (var integer i := 0; i < lengthof(stl); i := i + 1) {
474 if (match(stl[i], PFCP_session_active)) {
475 log("Active session: ", stl[i]);
476 active := active + 1;
477 }
478 }
479 if (active > 0) {
480 setverdict(fail, "There are still active sessions");
481 mtc.stop;
482 }
483 setverdict(pass);
484}
485
Pau Espin Pedrol601b5cd2024-04-17 19:16:17 +0200486private function f_vty_netinst_cfg(TELNETasp_PT vty_pt, Misc_Helpers.ro_charstring netinst_cmds)
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100487{
488 f_vty_enter_config(vty_pt);
489 f_vty_transceive(vty_pt, "netinst");
490 for (var integer i := 0; i < lengthof(netinst_cmds); i := i + 1) {
491 f_vty_transceive(vty_pt, netinst_cmds[i]);
492 }
493 f_vty_transceive_ret(vty_pt, "end");
494}
495
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200496function f_init_vty(charstring id := "foo") runs on test_CT {
497 if (UPFVTY.checkstate("Mapped")) {
498 /* skip initialization if already executed once */
499 return;
500 }
501 map(self:UPFVTY, system:UPFVTY);
502 f_vty_set_prompts(UPFVTY);
503 f_vty_transceive(UPFVTY, "enable");
504}
505
506/* global initialization function */
507function f_init(float guard_timeout := 30.0) runs on test_CT {
508 var integer bssap_idx;
509
510 T_guard.start(guard_timeout);
511 activate(as_Tguard());
512
513 f_init_vty("VirtCPF");
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100514
515 /* Clear out and set up default network instance config */
516 f_vty_netinst_cfg(UPFVTY,
517 { "clear",
518 "add access " & mp_netinst_access_ip_1,
519 "add access2 " & mp_netinst_access_ip_2,
520 "add core " & mp_netinst_core_ip_1,
521 "add core2 " & mp_netinst_core_ip_2
522 });
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200523}
524
525friend function f_shutdown_helper() runs on test_CT {
526 all component.stop;
527 setverdict(pass);
528 mtc.stop;
529}
530
531private function f_gen_test_hdlr_pars() runs on test_CT return TestHdlrParams {
532 var TestHdlrParams pars := valueof(t_def_TestHdlrPars);
533 pars.remote_upf_addr := mp_pfcp_ip_upf;
534 pars.local_addr := mp_pfcp_ip_local;
535 pars.local_node_id := valueof(ts_PFCP_Node_ID_ipv4(f_inet_addr(mp_pfcp_ip_local)));
536 return pars;
537}
538
539type function void_fn(charstring id) runs on CPF_ConnHdlr;
540
541function f_start_handler_create(TestHdlrParams pars)
542runs on test_CT return CPF_ConnHdlr {
543 var charstring id := testcasename();
544 var CPF_ConnHdlr vc_conn;
545 vc_conn := CPF_ConnHdlr.create(id);
546 return vc_conn;
547}
548
549function f_start_handler_run(CPF_ConnHdlr vc_conn, void_fn fn, TestHdlrParams pars)
550runs on test_CT return CPF_ConnHdlr {
551 var charstring id := testcasename();
552 /* Emit a marker to appear in the SUT's own logging output */
553 f_logp(UPFVTY, id & "() start");
554 vc_conn.start(f_handler_init(fn, id, pars));
555 return vc_conn;
556}
557
558function f_start_handler(void_fn fn, template (omit) TestHdlrParams pars_tmpl := omit)
559runs on test_CT return CPF_ConnHdlr {
560 var TestHdlrParams pars;
561 if (isvalue(pars_tmpl)) {
562 pars := valueof(pars_tmpl);
563 } else {
564 pars := valueof(f_gen_test_hdlr_pars());
565 }
566 return f_start_handler_run(f_start_handler_create(pars), fn, pars);
567}
568
569/* first function inside ConnHdlr component; sets g_pars + starts function */
570private function f_handler_init(void_fn fn, charstring id, TestHdlrParams pars)
571runs on CPF_ConnHdlr {
572 f_CPF_ConnHdlr_init(id, pars);
573 fn.apply(id);
574}
575
576/* Run a PFCP Association procedure */
577private function f_assoc_setup() runs on CPF_ConnHdlr {
578 PFCP.send(ts_PFCP_Assoc_Setup_Req(g_pars.local_node_id, g_recovery_timestamp));
579 PFCP.receive(tr_PFCP_Assoc_Setup_Resp(cause := tr_PFCP_Cause(REQUEST_ACCEPTED)));
580}
581
582/* Release a PFCP Association */
583private function f_assoc_release() runs on CPF_ConnHdlr {
584 PFCP.send(ts_PFCP_Assoc_Release_Req(g_pars.local_node_id));
585 PFCP.receive(tr_PFCP_Assoc_Release_Resp(cause := tr_PFCP_Cause(REQUEST_ACCEPTED)));
586}
587
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100588/* Collection of what a test intends to send to osmo-upf */
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200589type record PFCP_Ruleset {
590 Create_PDR_list pdr,
591 Create_FAR_list far
592};
593
Neels Hofmeyr4a9015f2022-11-26 03:11:22 +0100594/* Add to r a rule set that does GTP decapsulation (half of encapsulation/decapsulation):
595 * Receive GTP on src_iface = ACCESS by a local F-TEID to be chosen by osmo-upf.
596 * Dispatch GTP payload as plain IP on dest_iface = CORE. */
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100597private function f_ruleset_add_GTP_decaps(inout PFCP_Ruleset r,
598 integer pdr_id,
599 charstring src_netinst,
600 integer far_id) {
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200601 r.pdr := r.pdr & {
602 valueof(
603 ts_PFCP_Create_PDR(
604 pdr_id,
605 ts_PFCP_PDI(
606 ACCESS,
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100607 local_F_TEID := ts_PFCP_F_TEID_choose_v4(),
608 network_instance := ts_PFCP_Network_Instance(src_netinst)),
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200609 ts_PFCP_Outer_Header_Removal(GTP_U_UDP_IPV4),
610 far_id
611 )
612 )
613 };
614 r.far := r.far & {
615 valueof(
616 ts_PFCP_Create_FAR(
617 far_id,
618 ts_PFCP_Apply_Action_FORW(),
619 valueof(ts_PFCP_Forwarding_Parameters(CORE))
620 )
621 )
622 };
623}
624
625/* Add to r a rule set that does GTP encapsulation (half of encapsulation/decapsulation) */
626private function f_ruleset_add_GTP_encaps(inout PFCP_Ruleset r,
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100627 integer pdr_id,
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200628 charstring ue_addr_v4 := "192.168.23.42",
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100629 integer far_id,
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200630 OCT4 remote_teid,
Neels Hofmeyr1e311462023-01-11 01:19:12 +0100631 OCT4 gtp_dest_addr_v4) {
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200632 r.pdr := r.pdr & {
633 valueof(
634 ts_PFCP_Create_PDR(
635 pdr_id,
636 ts_PFCP_PDI(
637 CORE,
Neels Hofmeyr1e311462023-01-11 01:19:12 +0100638 ue_addr_v4 := ts_PFCP_UE_IP_Address_v4(f_inet_addr(ue_addr_v4), is_destination := true)
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200639 ),
640 far_id := far_id
641 )
642 )
643 };
644 r.far := r.far & {
645 valueof(
646 ts_PFCP_Create_FAR(
647 far_id,
648 ts_PFCP_Apply_Action_FORW(),
649 valueof(ts_PFCP_Forwarding_Parameters(
650 ACCESS,
651 ts_PFCP_Outer_Header_Creation_GTP_ipv4(
652 remote_teid,
653 gtp_dest_addr_v4)
654 ))
655 )
656 )
657 };
658}
659
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100660/* Add to r a rule set that forwards GTP from one tunnel to another, i.e. one direction of a tunmap */
661private function f_ruleset_add_GTP_forw(inout PFCP_Ruleset r,
662 integer pdr_id,
663 e_PFCP_Src_Iface src_iface,
664 charstring src_netinst,
665 integer far_id,
666 e_PFCP_Dest_Iface dest_iface,
667 F_TEID dest_remote_f_teid) {
668 r.pdr := r.pdr & {
669 valueof(
670 ts_PFCP_Create_PDR(
671 pdr_id,
672 ts_PFCP_PDI(
673 src_iface,
674 local_F_TEID := ts_PFCP_F_TEID_choose_v4(),
675 network_instance := ts_PFCP_Network_Instance(src_netinst)),
676 ts_PFCP_Outer_Header_Removal(GTP_U_UDP_IPV4),
677 far_id
678 )
679 )
680 };
681 r.far := r.far & {
682 valueof(
683 ts_PFCP_Create_FAR(
684 far_id,
685 ts_PFCP_Apply_Action_FORW(),
686 valueof(ts_PFCP_Forwarding_Parameters(dest_iface,
687 ts_PFCP_Outer_Header_Creation_GTP_ipv4(dest_remote_f_teid.teid,
688 dest_remote_f_teid.ipv4_address)))
689 )
690 )
691 };
692}
693
694/* Add to r a DROP rule from src_iface to dest_iface */
695private function f_ruleset_add_GTP_drop(inout PFCP_Ruleset r,
696 integer pdr_id,
697 e_PFCP_Src_Iface src_iface,
698 charstring src_netinst,
699 integer far_id,
700 e_PFCP_Dest_Iface dest_iface) {
701 r.pdr := r.pdr & {
702 valueof(
703 ts_PFCP_Create_PDR(
704 pdr_id,
705 ts_PFCP_PDI(
706 src_iface,
707 local_F_TEID := ts_PFCP_F_TEID_choose_v4(),
708 network_instance := ts_PFCP_Network_Instance(src_netinst)),
709 ts_PFCP_Outer_Header_Removal(GTP_U_UDP_IPV4),
710 far_id
711 )
712 )
713 };
714 r.far := r.far & {
715 valueof(
716 ts_PFCP_Create_FAR(
717 far_id,
718 ts_PFCP_Apply_Action_DROP,
719 fp := omit
720 )
721 )
722 };
723}
724
725private function f_tunmap_upd_far_to_core(GTP_Action gtp) return Update_FAR
726{
727 return valueof(
728 ts_PFCP_Update_FAR(
729 gtp.core.tunmap.far_id,
730 ts_PFCP_Apply_Action_FORW(),
731 valueof(ts_PFCP_Update_Forwarding_Parameters(
732 CORE,
733 ts_PFCP_Outer_Header_Creation_GTP_ipv4(gtp.core.tunmap.teid_r,
734 f_inet_addr(gtp.core.tunmap.gtp_ip_r))
735 )
736 )
737 )
738 );
739}
740
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200741/* Return two PDR+FAR rulesets that involve a src=CP-Function. Such rulesets are emitted by certain third party CPF, and
742 * osmo-upf should ACK the creation but ignore the rules (no-op). This function models rulesets seen in the field, so we
743 * can confirm that osmo-upf ACKs and ignores. */
744private function f_ruleset_noop() return PFCP_Ruleset
745{
746 var PFCP_Ruleset r := { {}, {} };
747 var integer pdr_id := lengthof(r.pdr) + 1;
748 var integer far_id := lengthof(r.far) + 1;
749
750 r.pdr := r.pdr & {
751 valueof(
752 ts_PFCP_Create_PDR(
753 pdr_id,
754 ts_PFCP_PDI(
755 CP_FUNCTION,
756 local_F_TEID := ts_PFCP_F_TEID_choose_v4('17'O)),
757 ts_PFCP_Outer_Header_Removal(GTP_U_UDP_IPV4),
758 far_id
759 )
760 )
761 };
762 r.far := r.far & {
763 valueof(
764 ts_PFCP_Create_FAR(
765 far_id,
766 ts_PFCP_Apply_Action_FORW(),
767 valueof(ts_PFCP_Forwarding_Parameters(ACCESS))
768 )
769 )
770 };
771
772 /* And another one (sic) */
773 pdr_id := lengthof(r.pdr) + 1;
774 far_id := lengthof(r.far) + 1;
775
776 r.pdr := r.pdr & {
777 valueof(
778 ts_PFCP_Create_PDR(
779 pdr_id,
780 ts_PFCP_PDI(
781 CP_FUNCTION,
782 local_F_TEID := ts_PFCP_F_TEID_choose_v4('2a'O)),
783 far_id := far_id
784 )
785 )
786 };
787 r.far := r.far & {
788 valueof(
789 ts_PFCP_Create_FAR(
790 far_id,
791 ts_PFCP_Apply_Action_FORW(),
792 valueof(ts_PFCP_Forwarding_Parameters(ACCESS))
793 )
794 )
795 };
796 return r;
797}
798
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100799/* Return a rule set that does GTP encapsulation and decapsulation, in both directions. */
800private function f_ruleset_tunend(GTP_Action gtp, charstring netinst_access := "access") return PFCP_Ruleset
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200801{
802 var PFCP_Ruleset rules := { {}, {} };
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100803 f_ruleset_add_GTP_decaps(rules,
804 pdr_id := gtp.access.pdr_id,
805 src_netinst := netinst_access,
806 far_id := gtp.core.tunend.far_id);
807 f_ruleset_add_GTP_encaps(rules,
808 gtp.core.tunend.pdr_id,
809 gtp.core.tunend.ip_l,
810 gtp.access.far_id,
811 gtp.access.teid_r,
812 f_inet_addr(gtp.access.gtp_ip_r));
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200813 return rules;
814}
815
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100816/* Return a rule set that does GTP tunnel forwarding in both directions.
817 * If core_gtp_known == true, place full FORW rules in both directions.
818 * If core_gtp_known == false, keep the Core side as DROP: this allows testing the usual/realistic case, where upon
819 * Session Establishment, the core side PGW has not yet provided the destination GTP F-TEID, which will follow later in
820 * a Session Modification. (This test suite has already configured which GTP F-TEID will be used on the core side, but
821 * we're omitting it from Session Establishment, until it is time to use it in f_session_mod()). */
822private function f_ruleset_tunmap(GTP_Action gtp, boolean core_gtp_known := true,
823 charstring netinst_access := "access",
824 charstring netinst_core := "core") return PFCP_Ruleset
825{
826 var PFCP_Ruleset rules := { {}, {} };
827 /* Access to Core */
828 if (core_gtp_known) {
829 f_ruleset_add_GTP_forw(rules,
830 pdr_id := gtp.access.pdr_id,
831 src_iface := ACCESS,
832 src_netinst := netinst_access,
833 far_id := gtp.core.tunmap.far_id,
834 dest_iface := CORE,
835 dest_remote_f_teid := valueof(ts_PFCP_F_TEID_ipv4(gtp.core.tunmap.teid_r,
836 f_inet_addr(gtp.core.tunmap.gtp_ip_r))));
837 } else {
838 /* The Core remote GTP will follow in a Session Modification, for now set Core->Access to DROP */
839 f_ruleset_add_GTP_drop(rules,
840 pdr_id := gtp.access.pdr_id,
841 src_iface := ACCESS,
842 src_netinst := netinst_access,
843 far_id := gtp.core.tunmap.far_id,
844 dest_iface := CORE);
845 }
846 /* Core to Access */
847 f_ruleset_add_GTP_forw(rules,
848 pdr_id := gtp.core.tunmap.pdr_id,
849 src_iface := CORE,
850 src_netinst := netinst_core,
851 far_id := gtp.access.far_id,
852 dest_iface := ACCESS,
853 dest_remote_f_teid := valueof(ts_PFCP_F_TEID_ipv4(gtp.access.teid_r,
854 f_inet_addr(gtp.access.gtp_ip_r))));
855 return rules;
856}
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200857
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100858/* From a PFCP Session Establishment Response, retrieve the F_TEID returned in the Created PDR IE for the given PDR Id
859 */
860private function f_get_created_local_f_teid(PDU_PFCP sess_est_resp, integer pdr_id) return F_TEID
861{
862 for (var integer i := 0;
863 i < lengthof(sess_est_resp.message_body.pfcp_session_establishment_response.created_PDR_list);
864 i := i + 1) {
865 var Created_PDR cpdr := sess_est_resp.message_body.pfcp_session_establishment_response.created_PDR_list[i];
866 if (oct2int(cpdr.grouped_ie.pdr_id.rule_id) != pdr_id) {
867 continue;
868 }
869 log("osmo-upf has chosen local F-TEID: PDR-" & int2str(pdr_id) & " = ", cpdr.grouped_ie.local_F_TEID);
870 return cpdr.grouped_ie.local_F_TEID;
871 }
872 setverdict(fail, "PDR Id " & int2str(pdr_id) & " not found in PFCP message");
873 mtc.stop;
874}
875
876/* Run a PFCP Session Establishment procedure */
877private function f_session_est(inout PFCP_session s, PFCP_Ruleset rules) runs on CPF_ConnHdlr
878{
879 log("f_session_est: rules = ", rules);
Neels Hofmeyr1e311462023-01-11 01:19:12 +0100880 PFCP.send(ts_PFCP_Session_Est_Req(ts_PFCP_Node_ID_ipv4(f_inet_addr(g_pars.local_addr)),
881 ts_PFCP_F_SEID_ipv4(f_inet_addr(g_pars.local_addr), s.cp_seid),
882 rules.pdr, rules.far));
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200883
884 var PDU_PFCP pfcp;
885 PFCP.receive(tr_PFCP_Session_Est_Resp(s.cp_seid)) -> value pfcp;
886 s.up_seid := pfcp.message_body.pfcp_session_establishment_response.UP_F_SEID.seid;
887 s.gtp.seid_l := s.up_seid;
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100888
889 var F_TEID access_local_f_teid := f_get_created_local_f_teid(pfcp, s.gtp.access.pdr_id);
890 s.gtp.access.gtp_ip_l := f_inet_ntoa(access_local_f_teid.ipv4_address);
891 s.gtp.access.teid_l := access_local_f_teid.teid;
892
893 if (ischosen(s.gtp.core.tunmap)) {
894 var F_TEID core_local_f_teid := f_get_created_local_f_teid(pfcp, s.gtp.core.tunmap.pdr_id);
895 s.gtp.core.tunmap.gtp_ip_l := f_inet_ntoa(core_local_f_teid.ipv4_address);
896 s.gtp.core.tunmap.teid_l := core_local_f_teid.teid;
897 }
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200898 log("established PFCP session: ", s);
899}
900
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100901/* Run a PFCP Session Modification procedure */
902private function f_session_mod(inout PFCP_session s) runs on CPF_ConnHdlr
903{
904 PFCP.send(ts_PFCP_Session_Mod_Req(s.up_seid, f_tunmap_upd_far_to_core(s.gtp)));
905 PFCP.receive(tr_PFCP_Session_Mod_Resp(s.cp_seid));
906 log("modified PFCP session: ", s);
907}
908
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +0100909private function f_create_PFCP_session_tunend() runs on CPF_ConnHdlr return PFCP_session
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200910{
911 var PFCP_session s := {
912 up_seid := -,
913 cp_seid := f_next_seid(),
914 gtp := {
Neels Hofmeyr366cdc72022-11-21 16:28:00 +0100915 kind := "tunend",
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100916 access := {
917 pdr_id := 1,
918 /* gtp_ip_l and teid_l will be returned by Session Establishment Response, Created PDR */
919 gtp_ip_l := "",
920 teid_l := '00000000'O,
921 far_id := 2,
922 gtp_ip_r := "127.0.0.2",
923 teid_r := f_next_remote_teid()
924 },
925 core := {
926 tunend := {
927 pdr_id := 2,
928 ip_l := f_next_ue_addr(),
929 far_id := 1
930 }
931 },
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200932 pfcp_peer := g_pars.local_addr,
933 seid_l := '0000000000000000'O
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100934 /* seid_l will be returned by Session Establishment Response */
935 }
936 };
937 return s;
938}
939
940private function f_create_PFCP_session_tunmap() runs on CPF_ConnHdlr return PFCP_session
941{
942 var PFCP_session s := {
943 up_seid := -,
944 cp_seid := f_next_seid(),
945 gtp := {
946 kind := "tunmap",
947 access := {
948 pdr_id := 1,
949 /* gtp_ip_l and teid_l will be returned by Session Establishment Response, Created PDR */
950 gtp_ip_l := "",
951 teid_l := '00000000'O,
952 far_id := 2,
953 gtp_ip_r := "127.0.0.2",
954 teid_r := f_next_remote_teid()
955 },
956 core := {
957 tunmap := {
958 pdr_id := 2,
959 /* gtp_ip_l and teid_l will be returned by Session Establishment Response, Created PDR */
960 gtp_ip_l := "",
961 teid_l := '00000000'O,
962 far_id := 1,
963 gtp_ip_r := "127.0.0.3",
964 teid_r := f_next_remote_teid()
965 }
966 },
967 pfcp_peer := g_pars.local_addr,
968 seid_l := '0000000000000000'O
969 /* seid_l will be returned by Session Establishment Response */
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200970 }
971 };
972 return s;
973}
974
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +0100975/* Do a PFCP Session Establishment with default values (see f_create_PFCP_session_tunend()) */
976private function f_session_est_tunend() runs on CPF_ConnHdlr return PFCP_session
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200977{
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +0100978 var PFCP_session s := f_create_PFCP_session_tunend();
Neels Hofmeyr366cdc72022-11-21 16:28:00 +0100979 f_session_est(s, f_ruleset_tunend(s.gtp));
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200980 return s;
981}
982
983private function f_session_del(PFCP_session s) runs on CPF_ConnHdlr {
984 PFCP.send(ts_PFCP_Session_Del_Req(s.up_seid));
985 PFCP.receive(tr_PFCP_Session_Del_Resp(s.cp_seid));
986}
987
988private function f_tc_assoc(charstring id) runs on CPF_ConnHdlr {
989 f_assoc_setup();
990 f_assoc_release();
991 setverdict(pass);
992}
993
994/* Verify that the CPF can send a Node-ID of the IPv4 type */
995testcase TC_assoc_node_id_v4() runs on test_CT {
996 var CPF_ConnHdlr vc_conn;
997
998 f_init(guard_timeout := 5.0);
999 vc_conn := f_start_handler(refers(f_tc_assoc));
1000 vc_conn.done;
1001 f_shutdown_helper();
1002}
1003
1004/* Verify that the CPF can send a Node-ID of the FQDN type */
1005testcase TC_assoc_node_id_fqdn() runs on test_CT {
1006 var CPF_ConnHdlr vc_conn;
1007 var TestHdlrParams pars := f_gen_test_hdlr_pars();
1008
1009 pars.local_node_id := valueof(ts_PFCP_Node_ID_fqdn("\7example\3com"));
1010
1011 f_init(guard_timeout := 5.0);
1012 vc_conn := f_start_handler(refers(f_tc_assoc), pars);
1013 vc_conn.done;
1014 f_shutdown_helper();
1015}
1016
1017/* Verify PFCP Session Establishment and Deletion */
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001018private function f_tc_session_est_tunend(charstring id) runs on CPF_ConnHdlr {
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001019 f_assoc_setup();
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001020 var PFCP_session s := f_session_est_tunend();
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001021 f_sleep(1.0);
1022 f_vty_expect_session_active(UPFVTY, s);
1023 f_session_del(s);
1024 f_vty_expect_no_active_sessions(UPFVTY);
1025 f_vty_expect_no_gtp_actions(UPFVTY);
1026 f_assoc_release();
1027 setverdict(pass);
1028}
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001029testcase TC_session_est_tunend() runs on test_CT {
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001030 var CPF_ConnHdlr vc_conn;
1031
1032 f_init(guard_timeout := 15.0);
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001033 vc_conn := f_start_handler(refers(f_tc_session_est_tunend));
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001034 vc_conn.done;
1035 f_shutdown_helper();
1036}
1037
1038/* Verify that releasing a PFCP Association also releases all its sessions and GTP actions. */
1039private function f_tc_session_term_by_assoc_rel(charstring id) runs on CPF_ConnHdlr {
1040 f_assoc_setup();
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001041 var PFCP_session s := f_session_est_tunend();
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001042 f_sleep(1.0);
1043 f_vty_expect_session_active(UPFVTY, s);
1044 f_assoc_release();
1045 f_vty_expect_no_active_sessions(UPFVTY);
1046 f_vty_expect_no_gtp_actions(UPFVTY);
1047 setverdict(pass);
1048}
1049testcase TC_session_term_by_assoc_rel() runs on test_CT {
1050 var CPF_ConnHdlr vc_conn;
1051
1052 f_init(guard_timeout := 15.0);
1053 vc_conn := f_start_handler(refers(f_tc_session_term_by_assoc_rel));
1054 vc_conn.done;
1055 f_shutdown_helper();
1056}
1057
1058/* Verify that PFCP Sessions with a src-interface other than ACCESS or CORE are ACKed by osmo-upf but have no effect. */
1059private function f_tc_session_est_noop(charstring id) runs on CPF_ConnHdlr {
1060 f_assoc_setup();
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001061 var PFCP_session s := f_create_PFCP_session_tunend();
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001062 f_session_est(s, f_ruleset_noop());
1063
1064 f_sleep(1.0);
1065 f_vty_expect_session_status(UPFVTY, s, PFCP_session_inactive);
1066
1067 f_session_del(s);
1068 f_vty_expect_no_active_sessions(UPFVTY);
1069 f_vty_expect_no_gtp_actions(UPFVTY);
1070 f_assoc_release();
1071 setverdict(pass);
1072}
1073testcase TC_session_est_noop() runs on test_CT {
1074 var CPF_ConnHdlr vc_conn;
1075
1076 f_init(guard_timeout := 15.0);
1077 vc_conn := f_start_handler(refers(f_tc_session_est_noop));
1078 vc_conn.done;
1079 f_shutdown_helper();
1080}
1081
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +01001082/* Verify that the Network Instance IE in Create PDR chooses the right local address for a tunmap session */
1083private function f_tc_session_est_tunmap(charstring id) runs on CPF_ConnHdlr {
1084 f_assoc_setup();
1085 var PFCP_session s := f_create_PFCP_session_tunmap();
1086 f_session_est(s, f_ruleset_tunmap(s.gtp));
1087 f_sleep(1.0);
1088 f_vty_expect_session_active(UPFVTY, s);
1089 f_session_del(s);
1090 f_vty_expect_no_active_sessions(UPFVTY);
1091 f_vty_expect_no_gtp_actions(UPFVTY);
1092 f_assoc_release();
1093 setverdict(pass);
1094}
1095testcase TC_session_est_tunmap() runs on test_CT {
1096 var CPF_ConnHdlr vc_conn;
1097
1098 f_init(guard_timeout := 15.0);
1099
1100 vc_conn := f_start_handler(refers(f_tc_session_est_tunmap));
1101 vc_conn.done;
1102 f_shutdown_helper();
1103}
1104
1105/* Set up a tunmap session with a partial Session Establishment, followed by a Session Modification to complete it. */
1106private function f_session_est_mod_tunmap(charstring netinst_access, charstring expect_gtp_ip_access,
1107 charstring netinst_core, charstring expect_gtp_ip_core) runs on CPF_ConnHdlr {
1108 f_assoc_setup();
1109 var PFCP_session s := f_create_PFCP_session_tunmap();
1110 f_session_est(s, f_ruleset_tunmap(s.gtp, core_gtp_known := false,
1111 netinst_access := netinst_access, netinst_core := netinst_core));
1112 /* The locally chosen GTP IP addresses where osmo-upf receives GTP traffic were chosen by netinst_access /
1113 * netinst_core and are returned in s.gtp.access.gtp_ip_l / s.gtp.core.tunmap.gtp_ip_l. Verify that the netinst
1114 * names have returned their matching IP addresses. */
1115 if (s.gtp.access.gtp_ip_l != expect_gtp_ip_access) {
1116 setverdict(fail, "Network Instance '" & netinst_access & "' should have yielded GTP IP " &
1117 expect_gtp_ip_access & " but osmo-upf chose " & s.gtp.access.gtp_ip_l);
1118 mtc.stop;
1119 }
1120 if (s.gtp.core.tunmap.gtp_ip_l != expect_gtp_ip_core) {
1121 setverdict(fail, "Network Instance '" & netinst_core & "' should have yielded GTP IP " &
1122 expect_gtp_ip_core & " but osmo-upf chose " & s.gtp.core.tunmap.gtp_ip_l);
1123 mtc.stop;
1124 }
1125
1126 f_sleep(1.0);
1127 f_vty_expect_session_status(UPFVTY, s, PFCP_session_inactive);
1128
1129 f_session_mod(s);
1130 f_sleep(1.0);
1131 f_vty_expect_session_active(UPFVTY, s);
1132 f_session_del(s);
1133 f_vty_expect_no_active_sessions(UPFVTY);
1134 f_vty_expect_no_gtp_actions(UPFVTY);
1135 f_assoc_release();
1136 setverdict(pass);
1137}
1138/* Run f_session_est_mod_tunmap() with the first Network Instances */
1139private function f_tc_session_est_mod_tunmap(charstring id) runs on CPF_ConnHdlr {
1140 f_session_est_mod_tunmap("access", mp_netinst_access_ip_1, "core", mp_netinst_core_ip_1);
1141}
1142/* Run f_session_est_mod_tunmap() with the second Network Instances */
1143private function f_tc_session_est_mod_tunmap2(charstring id) runs on CPF_ConnHdlr {
1144 f_session_est_mod_tunmap("access2", mp_netinst_access_ip_2, "core2", mp_netinst_core_ip_2);
1145}
1146testcase TC_session_est_mod_tunmap() runs on test_CT {
1147 var CPF_ConnHdlr vc_conn;
1148
1149 f_init(guard_timeout := 15.0);
1150
1151 vc_conn := f_start_handler(refers(f_tc_session_est_mod_tunmap));
1152 vc_conn.done;
1153 f_shutdown_helper();
1154}
1155testcase TC_session_est_mod_tunmap2() runs on test_CT {
1156 var CPF_ConnHdlr vc_conn;
1157
1158 f_init(guard_timeout := 15.0);
1159
1160 vc_conn := f_start_handler(refers(f_tc_session_est_mod_tunmap2));
1161 vc_conn.done;
1162 f_shutdown_helper();
1163}
1164
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001165control {
1166 execute( TC_assoc_node_id_v4() );
1167 execute( TC_assoc_node_id_fqdn() );
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001168 execute( TC_session_est_tunend() );
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001169 execute( TC_session_term_by_assoc_rel() );
1170 execute( TC_session_est_noop() );
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +01001171 execute( TC_session_est_tunmap() );
1172 execute( TC_session_est_mod_tunmap() );
1173 execute( TC_session_est_mod_tunmap2() );
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001174}
1175
1176}