blob: 7dc01bb5dd9e7cda1ead190619fbd282040be711 [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
71friend function f_logp(TELNETasp_PT pt, charstring log_msg)
72{
73 // log on TTCN3 log output
74 log(log_msg);
75 // log in stderr log
76 f_vty_transceive(pt, "logp lglobal notice TTCN3 f_logp(): " & log_msg);
77}
78
Neels Hofmeyr2d292742022-06-07 23:58:05 +020079private function f_get_name_val(out charstring val, charstring str, charstring name, charstring sep := ":", charstring delim := " ") return boolean {
80 var charstring labl := name & sep;
81 var integer namepos := f_strstr(str, labl);
82 if (namepos < 0) {
83 return false;
84 }
85 var integer valpos := namepos + lengthof(labl);
86 var integer valend := f_strstr(str, delim, valpos);
87 if (valend < 0) {
88 valend := lengthof(str);
89 }
90 val := substr(str, valpos, valend - valpos);
91 return true;
92}
93
94private function f_get_name_val_oct8(out OCT8 val, charstring str, charstring name) return boolean {
95 var charstring token;
96 if (not f_get_name_val(token, str, name, ":0x")) {
97 return false;
98 }
99 if (lengthof(token) > 16) {
100 log("token too long: ", name, " in ", str);
101 return false;
102 }
103 var charstring padded := substr("0000000000000000", 0, 16 - lengthof(token)) & token;
104 val := str2oct(padded);
105 return true;
106}
107
108private function f_get_name_val_oct4(out OCT4 val, charstring str, charstring name) return boolean {
109 var charstring token;
110 if (not f_get_name_val(token, str, name, ":0x")) {
111 return false;
112 }
113 if (lengthof(token) > 8) {
114 log("token too long: ", name, " in ", str);
115 return false;
116 }
117 var charstring padded := substr("00000000", 0, 8 - lengthof(token)) & token;
118 val := str2oct(padded);
119 return true;
120}
121
122private function f_get_name_val_int(out integer val, charstring str, charstring name) return boolean {
123 var charstring token;
124 if (not f_get_name_val(token, str, name)) {
125 return false;
126 }
127 val := str2int(token);
128 return true;
129}
130
131private function f_get_name_val_2int(out integer val1, out integer val2, charstring str, charstring name, charstring delim := ",") return boolean {
132 var charstring token;
133 if (not f_get_name_val(token, str, name)) {
134 return false;
135 }
136 var ro_charstring nrl := f_str_split(token, delim);
137 if (lengthof(nrl) != 2) {
138 return false;
139 }
140 val1 := str2int(nrl[0]);
141 val2 := str2int(nrl[1]);
142 return true;
143}
144
145/* A PFCP session as seen by the system under test, osmo-upf. up_seid is what osmo-upf sees as its local SEID
146 * ("SEID-l"). cp_seid is this tester's side's SEID, which osmo-upf sees as the remote SEID. */
147type record PFCP_session {
148 OCT8 up_seid,
149 OCT8 cp_seid,
150 GTP_Action gtp
151}
152
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100153/* _r and _l means 'remote' and 'local', from the perspective of the osmo-upf process. */
154type record GTP_Action_tunend {
155 /* the PDR Id detecting packets from this side */
156 integer pdr_id,
157 /* IP packets arriving from this side are arriving on ip_l */
158 charstring ip_l,
159
160 /* the FAR Id forwarding packets to this side */
161 integer far_id
162}
163
164/* _r and _l means 'remote' and 'local', from the perspective of the osmo-upf process. */
165type record GTP_Action_tun {
166 /* the PDR Id detecting packets from this side */
167 integer pdr_id,
168 /* GTP arriving from this side is arriving on gtp_ip_l with teid_l */
169 charstring gtp_ip_l,
170 OCT4 teid_l,
171
172 /* the FAR Id forwarding packets to this side */
173 integer far_id,
174 /* GTP going out to this side should be sent to gtp_ip_r with teid_r */
175 charstring gtp_ip_r,
176 OCT4 teid_r
177}
178
179type union GTP_Action_core {
180 /* For kind = "tunend", the local IP that the UE has on 'the internet' */
181 GTP_Action_tunend tunend,
182 /* For kind = "tunmap", the second GTP tunnel */
183 GTP_Action_tun tunmap
184}
185
186/* State of what GTP functionality osmo-upf should put in place, after a PFCP request was ACKed by it.
187 * _r and _l means 'remote' and 'local', from the perspective of the osmo-upf process.
188 *
189 * tunend:
190 * Access UPF Core
191 * GTP-r:127.0.0.2,0x1 <-FAR-1-- | 192.168.0.1 <-PDR-1--
192 * --PDR-2-> GTP-l:127.0.0.1,0x2 | --FAR-2-> (IP destination is in each GTP payload)
193 *
194 * tunmap:
195 * Access UPF Core
196 * GTP-r:127.0.0.2,0x1 <-FAR-1-- | 127.0.0.1,0x1 <-PDR-1--
197 * --PDR-2-> GTP-l:127.0.0.1,0x2 | --FAR-2-> GTP-r:127.0.0.3,0x2
198 */
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200199type record GTP_Action {
Neels Hofmeyr366cdc72022-11-21 16:28:00 +0100200 /* kind = ("tunend"|"tunmap") */
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200201 charstring kind,
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100202 /* The access side GTP tunnel. (The 'Access' side is always GTP.) */
203 GTP_Action_tun access,
204 /* The core side GTP tunnel (tunmap) or local IP (tunend) */
205 GTP_Action_core core,
206 /* 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 +0200207 charstring pfcp_peer,
208 OCT8 seid_l
209};
210
211type record of GTP_Action GTP_Action_List;
212
213private function f_parse_gtp_action(out GTP_Action ret, charstring str) return boolean {
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100214 /* Parse a string like
215 * "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"
216 */
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200217 var GTP_Action a;
218 if (not f_get_name_val(a.kind, str, "GTP")) {
219 return false;
220 }
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100221 if (not f_get_name_val(a.access.gtp_ip_r, str, "GTP-access-r")) {
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200222 return false;
223 }
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100224 if (not f_get_name_val_oct4(a.access.teid_r, str, "TEID-access-r")) {
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200225 return false;
226 }
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100227 if (not f_get_name_val(a.access.gtp_ip_l, str, "GTP-access-l")) {
228 return false;
229 }
230 if (not f_get_name_val_oct4(a.access.teid_l, str, "TEID-access-l")) {
231 return false;
232 }
233 if (not f_get_name_val_int(a.access.pdr_id, str, "PDR-access")) {
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200234 return false;
235 }
236 if (not f_get_name_val(a.pfcp_peer, str, "PFCP-peer")) {
237 return false;
238 }
239 if (not f_get_name_val_oct8(a.seid_l, str, "SEID-l")) {
240 return false;
241 }
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100242 if (a.kind == "tunend") {
243 if (not f_get_name_val_int(a.core.tunend.pdr_id, str, "PDR-core")) {
244 return false;
245 }
246 if (not f_get_name_val(a.core.tunend.ip_l, str, "IP-core-l")) {
247 return false;
248 }
249 /* in these tests, the PDR Id and its FAR Id are always the same: PDR for incoming on Access matches its
250 * FAR that forwards to Core. */
251 a.core.tunend.far_id := a.access.pdr_id;
252 a.access.far_id := a.core.tunend.pdr_id;
253 } else if (a.kind == "tunmap") {
254 if (not f_get_name_val(a.core.tunmap.gtp_ip_r, str, "GTP-core-r")) {
255 return false;
256 }
257 if (not f_get_name_val_oct4(a.core.tunmap.teid_r, str, "TEID-core-r")) {
258 return false;
259 }
260 if (not f_get_name_val(a.core.tunmap.gtp_ip_l, str, "GTP-core-l")) {
261 return false;
262 }
263 if (not f_get_name_val_oct4(a.core.tunmap.teid_l, str, "TEID-core-l")) {
264 return false;
265 }
266 if (not f_get_name_val_int(a.core.tunmap.pdr_id, str, "PDR-core")) {
267 return false;
268 }
269 /* in these tests, the PDR Id and its FAR Id are always the same: PDR for incoming on Access matches its
270 * FAR that forwards to Core. */
271 a.core.tunmap.far_id := a.access.pdr_id;
272 a.access.far_id := a.core.tunmap.pdr_id;
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200273 }
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100274
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200275 ret := a;
276 return true;
277}
278
279private function f_vty_get_gtp_actions(TELNETasp_PT vty_pt) return GTP_Action_List {
280 var charstring gtp_str := f_vty_transceive_ret(vty_pt, "show gtp");
281 var ro_charstring lines := f_str_split(gtp_str, "\n");
282 var GTP_Action_List gtps := {};
283 for (var integer i := 0; i < lengthof(lines); i := i + 1) {
284 var charstring line := lines[i];
285 var GTP_Action a;
286 if (not f_parse_gtp_action(a, line)) {
287 continue;
288 }
289 gtps := gtps & { a };
290 }
291 log("GTP-actions: ", gtps);
292 return gtps;
293}
294
295private function f_find_gtp_action(GTP_Action_List actions, template GTP_Action find) return boolean {
296 for (var integer i := 0; i < lengthof(actions); i := i + 1) {
297 if (match(actions[i], find)) {
298 return true;
299 }
300 }
301 return false;
302}
303
304private function f_expect_gtp_action(GTP_Action_List actions, template GTP_Action expect) {
305 if (f_find_gtp_action(actions, expect)) {
306 log("VTY confirms: GTP action active: ", expect);
307 setverdict(pass);
308 return;
309 }
310 log("Expected to find ", expect, " in ", actions);
311 setverdict(fail, "on VTY, a GTP action failed to show as active");
312 mtc.stop;
313}
314
315private function f_expect_no_gtp_action(GTP_Action_List actions, template GTP_Action expect) {
316 if (f_find_gtp_action(actions, expect)) {
317 log("Expected to *not* find ", expect, " in ", actions);
318 setverdict(fail, "a GTP action failed to show as inactive");
319 mtc.stop;
320 }
321 log("VTY confirms: GTP action inactive: ", expect);
322 setverdict(pass);
323 return;
324}
325
326private function f_vty_expect_gtp_action(TELNETasp_PT vty_pt, template GTP_Action expect) {
327 if (not mp_verify_gtp_actions) {
328 /* In GTP mockup mode, GTP actions don't show on VTY. Cannot verify. */
329 setverdict(pass);
330 return;
331 }
332 var GTP_Action_List actions := f_vty_get_gtp_actions(vty_pt);
333 f_expect_gtp_action(actions, expect);
334}
335
336private function f_vty_expect_no_gtp_actions(TELNETasp_PT vty_pt) {
337 var GTP_Action_List actions := f_vty_get_gtp_actions(vty_pt);
338 if (lengthof(actions) > 0) {
339 setverdict(fail, "VTY says that there are still active GTP actions");
340 mtc.stop;
341 }
342 setverdict(pass);
343}
344
345type record PFCP_Session_Status {
346 charstring peer,
347 OCT8 seid_r,
348 OCT8 seid_l,
349 charstring state,
350 integer pdr_active_count,
351 integer pdr_count,
352 integer far_active_count,
353 integer far_count,
354 integer gtp_active_count
355};
356
357template PFCP_Session_Status PFCP_session_active := {
358 peer := ?,
359 seid_r := ?,
360 seid_l := ?,
361 state := "ESTABLISHED",
362 pdr_active_count := (1..99999),
363 pdr_count := (1..99999),
364 far_active_count := (1..99999),
365 far_count := (1..99999),
366 gtp_active_count := (1..99999)
367};
368
369template PFCP_Session_Status PFCP_session_inactive := {
370 peer := ?,
371 seid_r := ?,
372 seid_l := ?,
373 state := "ESTABLISHED",
374 pdr_active_count := 0,
375 pdr_count := (1..99999),
376 far_active_count := 0,
377 far_count := (1..99999),
378 gtp_active_count := 0
379};
380
381type record of PFCP_Session_Status PFCP_Session_Status_List;
382
383private function f_parse_session_status(out PFCP_Session_Status ret, charstring str) return boolean {
384 var PFCP_Session_Status st;
385 if (not f_get_name_val(st.peer, str, "peer")) {
386 return false;
387 }
388 if (not f_get_name_val_oct8(st.seid_l, str, "SEID-l")) {
389 return false;
390 }
391 f_get_name_val_oct8(st.seid_r, str, "SEID-r");
392 f_get_name_val(st.state, str, "state");
393
394 /* parse 'PDR-active:1/2' */
395 if (not f_get_name_val_2int(st.pdr_active_count, st.pdr_count, str, "PDR-active", "/")) {
396 return false;
397 }
398 /* parse 'FAR-active:1/2' */
399 if (not f_get_name_val_2int(st.far_active_count, st.far_count, str, "FAR-active", "/")) {
400 return false;
401 }
402
403 f_get_name_val_int(st.gtp_active_count, str, "GTP-active");
404 ret := st;
405 return true;
406}
407
408private function f_vty_get_sessions(TELNETasp_PT vty_pt) return PFCP_Session_Status_List {
409 var charstring sessions_str := f_vty_transceive_ret(vty_pt, "show session");
410 var ro_charstring lines := f_str_split(sessions_str, "\n");
411 var PFCP_Session_Status_List sessions := {};
412 for (var integer i := 0; i < lengthof(lines); i := i + 1) {
413 var charstring line := lines[i];
414 var PFCP_Session_Status st;
415 if (not f_parse_session_status(st, line)) {
416 continue;
417 }
418 sessions := sessions & { st };
419 }
420 log("Sessions: ", sessions);
421 return sessions;
422}
423
424private function f_vty_get_session_status(TELNETasp_PT vty_pt, PFCP_session s, out PFCP_Session_Status ret) return boolean {
425 var PFCP_Session_Status_List sessions := f_vty_get_sessions(vty_pt);
426 return f_get_session_status(sessions, s, ret);
427}
428
429private function f_get_session_status(PFCP_Session_Status_List sessions, PFCP_session s, out PFCP_Session_Status ret)
430return boolean {
431 var PFCP_Session_Status_List matches := {};
432 for (var integer i := 0; i < lengthof(sessions); i := i + 1) {
433 var PFCP_Session_Status st := sessions[i];
434 if (st.seid_l != s.up_seid) {
435 continue;
436 }
437 if (st.seid_r != s.cp_seid) {
438 continue;
439 }
440 matches := matches & { st };
441 }
442 if (lengthof(matches) < 1) {
443 log("no session with SEID-l = ", s.up_seid);
444 return false;
445 }
446 if (lengthof(matches) > 1) {
447 log("multiple sessions have ", s, ": ", matches);
448 return false;
449 }
450 ret := matches[0];
451 return true;
452}
453
454private function f_vty_expect_session_status(TELNETasp_PT vty_pt, PFCP_session s, template PFCP_Session_Status expect_st) {
455 var PFCP_Session_Status st;
456 if (not f_vty_get_session_status(vty_pt, s, st)) {
457 log("Session ", s, " not found in VTY session list");
458 setverdict(fail, "Session not found in VTY list");
459 mtc.stop;
460 }
461 log("Session ", s, " status: ", st);
462 if (not match(st, expect_st)) {
463 log("ERROR: Session ", st, " does not match ", expect_st);
464 setverdict(fail, "VTY shows unexpected state of PFCP session");
465 mtc.stop;
466 }
467
468 setverdict(pass);
469}
470
471private function f_vty_expect_session_active(TELNETasp_PT vty_pt, PFCP_session s)
472{
473 f_vty_expect_session_status(vty_pt, s, PFCP_session_active);
474 f_vty_expect_gtp_action(vty_pt, s.gtp);
475 setverdict(pass);
476}
477
478private function f_vty_expect_no_active_sessions(TELNETasp_PT vty_pt) {
479 var PFCP_Session_Status_List stl := f_vty_get_sessions(vty_pt);
480 var integer active := 0;
481 for (var integer i := 0; i < lengthof(stl); i := i + 1) {
482 if (match(stl[i], PFCP_session_active)) {
483 log("Active session: ", stl[i]);
484 active := active + 1;
485 }
486 }
487 if (active > 0) {
488 setverdict(fail, "There are still active sessions");
489 mtc.stop;
490 }
491 setverdict(pass);
492}
493
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100494private function f_vty_netinst_cfg(TELNETasp_PT vty_pt, ro_charstring netinst_cmds)
495{
496 f_vty_enter_config(vty_pt);
497 f_vty_transceive(vty_pt, "netinst");
498 for (var integer i := 0; i < lengthof(netinst_cmds); i := i + 1) {
499 f_vty_transceive(vty_pt, netinst_cmds[i]);
500 }
501 f_vty_transceive_ret(vty_pt, "end");
502}
503
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200504function f_init_vty(charstring id := "foo") runs on test_CT {
505 if (UPFVTY.checkstate("Mapped")) {
506 /* skip initialization if already executed once */
507 return;
508 }
509 map(self:UPFVTY, system:UPFVTY);
510 f_vty_set_prompts(UPFVTY);
511 f_vty_transceive(UPFVTY, "enable");
512}
513
514/* global initialization function */
515function f_init(float guard_timeout := 30.0) runs on test_CT {
516 var integer bssap_idx;
517
518 T_guard.start(guard_timeout);
519 activate(as_Tguard());
520
521 f_init_vty("VirtCPF");
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100522
523 /* Clear out and set up default network instance config */
524 f_vty_netinst_cfg(UPFVTY,
525 { "clear",
526 "add access " & mp_netinst_access_ip_1,
527 "add access2 " & mp_netinst_access_ip_2,
528 "add core " & mp_netinst_core_ip_1,
529 "add core2 " & mp_netinst_core_ip_2
530 });
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200531}
532
533friend function f_shutdown_helper() runs on test_CT {
534 all component.stop;
535 setverdict(pass);
536 mtc.stop;
537}
538
539private function f_gen_test_hdlr_pars() runs on test_CT return TestHdlrParams {
540 var TestHdlrParams pars := valueof(t_def_TestHdlrPars);
541 pars.remote_upf_addr := mp_pfcp_ip_upf;
542 pars.local_addr := mp_pfcp_ip_local;
543 pars.local_node_id := valueof(ts_PFCP_Node_ID_ipv4(f_inet_addr(mp_pfcp_ip_local)));
544 return pars;
545}
546
547type function void_fn(charstring id) runs on CPF_ConnHdlr;
548
549function f_start_handler_create(TestHdlrParams pars)
550runs on test_CT return CPF_ConnHdlr {
551 var charstring id := testcasename();
552 var CPF_ConnHdlr vc_conn;
553 vc_conn := CPF_ConnHdlr.create(id);
554 return vc_conn;
555}
556
557function f_start_handler_run(CPF_ConnHdlr vc_conn, void_fn fn, TestHdlrParams pars)
558runs on test_CT return CPF_ConnHdlr {
559 var charstring id := testcasename();
560 /* Emit a marker to appear in the SUT's own logging output */
561 f_logp(UPFVTY, id & "() start");
562 vc_conn.start(f_handler_init(fn, id, pars));
563 return vc_conn;
564}
565
566function f_start_handler(void_fn fn, template (omit) TestHdlrParams pars_tmpl := omit)
567runs on test_CT return CPF_ConnHdlr {
568 var TestHdlrParams pars;
569 if (isvalue(pars_tmpl)) {
570 pars := valueof(pars_tmpl);
571 } else {
572 pars := valueof(f_gen_test_hdlr_pars());
573 }
574 return f_start_handler_run(f_start_handler_create(pars), fn, pars);
575}
576
577/* first function inside ConnHdlr component; sets g_pars + starts function */
578private function f_handler_init(void_fn fn, charstring id, TestHdlrParams pars)
579runs on CPF_ConnHdlr {
580 f_CPF_ConnHdlr_init(id, pars);
581 fn.apply(id);
582}
583
584/* Run a PFCP Association procedure */
585private function f_assoc_setup() runs on CPF_ConnHdlr {
586 PFCP.send(ts_PFCP_Assoc_Setup_Req(g_pars.local_node_id, g_recovery_timestamp));
587 PFCP.receive(tr_PFCP_Assoc_Setup_Resp(cause := tr_PFCP_Cause(REQUEST_ACCEPTED)));
588}
589
590/* Release a PFCP Association */
591private function f_assoc_release() runs on CPF_ConnHdlr {
592 PFCP.send(ts_PFCP_Assoc_Release_Req(g_pars.local_node_id));
593 PFCP.receive(tr_PFCP_Assoc_Release_Resp(cause := tr_PFCP_Cause(REQUEST_ACCEPTED)));
594}
595
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100596/* Collection of what a test intends to send to osmo-upf */
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200597type record PFCP_Ruleset {
598 Create_PDR_list pdr,
599 Create_FAR_list far
600};
601
Neels Hofmeyr4a9015f2022-11-26 03:11:22 +0100602/* Add to r a rule set that does GTP decapsulation (half of encapsulation/decapsulation):
603 * Receive GTP on src_iface = ACCESS by a local F-TEID to be chosen by osmo-upf.
604 * Dispatch GTP payload as plain IP on dest_iface = CORE. */
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100605private function f_ruleset_add_GTP_decaps(inout PFCP_Ruleset r,
606 integer pdr_id,
607 charstring src_netinst,
608 integer far_id) {
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200609 r.pdr := r.pdr & {
610 valueof(
611 ts_PFCP_Create_PDR(
612 pdr_id,
613 ts_PFCP_PDI(
614 ACCESS,
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100615 local_F_TEID := ts_PFCP_F_TEID_choose_v4(),
616 network_instance := ts_PFCP_Network_Instance(src_netinst)),
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200617 ts_PFCP_Outer_Header_Removal(GTP_U_UDP_IPV4),
618 far_id
619 )
620 )
621 };
622 r.far := r.far & {
623 valueof(
624 ts_PFCP_Create_FAR(
625 far_id,
626 ts_PFCP_Apply_Action_FORW(),
627 valueof(ts_PFCP_Forwarding_Parameters(CORE))
628 )
629 )
630 };
631}
632
633/* Add to r a rule set that does GTP encapsulation (half of encapsulation/decapsulation) */
634private function f_ruleset_add_GTP_encaps(inout PFCP_Ruleset r,
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100635 integer pdr_id,
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200636 charstring ue_addr_v4 := "192.168.23.42",
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100637 integer far_id,
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200638 OCT4 remote_teid,
Neels Hofmeyr1e311462023-01-11 01:19:12 +0100639 OCT4 gtp_dest_addr_v4) {
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200640 r.pdr := r.pdr & {
641 valueof(
642 ts_PFCP_Create_PDR(
643 pdr_id,
644 ts_PFCP_PDI(
645 CORE,
Neels Hofmeyr1e311462023-01-11 01:19:12 +0100646 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 +0200647 ),
648 far_id := far_id
649 )
650 )
651 };
652 r.far := r.far & {
653 valueof(
654 ts_PFCP_Create_FAR(
655 far_id,
656 ts_PFCP_Apply_Action_FORW(),
657 valueof(ts_PFCP_Forwarding_Parameters(
658 ACCESS,
659 ts_PFCP_Outer_Header_Creation_GTP_ipv4(
660 remote_teid,
661 gtp_dest_addr_v4)
662 ))
663 )
664 )
665 };
666}
667
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100668/* Add to r a rule set that forwards GTP from one tunnel to another, i.e. one direction of a tunmap */
669private function f_ruleset_add_GTP_forw(inout PFCP_Ruleset r,
670 integer pdr_id,
671 e_PFCP_Src_Iface src_iface,
672 charstring src_netinst,
673 integer far_id,
674 e_PFCP_Dest_Iface dest_iface,
675 F_TEID dest_remote_f_teid) {
676 r.pdr := r.pdr & {
677 valueof(
678 ts_PFCP_Create_PDR(
679 pdr_id,
680 ts_PFCP_PDI(
681 src_iface,
682 local_F_TEID := ts_PFCP_F_TEID_choose_v4(),
683 network_instance := ts_PFCP_Network_Instance(src_netinst)),
684 ts_PFCP_Outer_Header_Removal(GTP_U_UDP_IPV4),
685 far_id
686 )
687 )
688 };
689 r.far := r.far & {
690 valueof(
691 ts_PFCP_Create_FAR(
692 far_id,
693 ts_PFCP_Apply_Action_FORW(),
694 valueof(ts_PFCP_Forwarding_Parameters(dest_iface,
695 ts_PFCP_Outer_Header_Creation_GTP_ipv4(dest_remote_f_teid.teid,
696 dest_remote_f_teid.ipv4_address)))
697 )
698 )
699 };
700}
701
702/* Add to r a DROP rule from src_iface to dest_iface */
703private function f_ruleset_add_GTP_drop(inout PFCP_Ruleset r,
704 integer pdr_id,
705 e_PFCP_Src_Iface src_iface,
706 charstring src_netinst,
707 integer far_id,
708 e_PFCP_Dest_Iface dest_iface) {
709 r.pdr := r.pdr & {
710 valueof(
711 ts_PFCP_Create_PDR(
712 pdr_id,
713 ts_PFCP_PDI(
714 src_iface,
715 local_F_TEID := ts_PFCP_F_TEID_choose_v4(),
716 network_instance := ts_PFCP_Network_Instance(src_netinst)),
717 ts_PFCP_Outer_Header_Removal(GTP_U_UDP_IPV4),
718 far_id
719 )
720 )
721 };
722 r.far := r.far & {
723 valueof(
724 ts_PFCP_Create_FAR(
725 far_id,
726 ts_PFCP_Apply_Action_DROP,
727 fp := omit
728 )
729 )
730 };
731}
732
733private function f_tunmap_upd_far_to_core(GTP_Action gtp) return Update_FAR
734{
735 return valueof(
736 ts_PFCP_Update_FAR(
737 gtp.core.tunmap.far_id,
738 ts_PFCP_Apply_Action_FORW(),
739 valueof(ts_PFCP_Update_Forwarding_Parameters(
740 CORE,
741 ts_PFCP_Outer_Header_Creation_GTP_ipv4(gtp.core.tunmap.teid_r,
742 f_inet_addr(gtp.core.tunmap.gtp_ip_r))
743 )
744 )
745 )
746 );
747}
748
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200749/* Return two PDR+FAR rulesets that involve a src=CP-Function. Such rulesets are emitted by certain third party CPF, and
750 * osmo-upf should ACK the creation but ignore the rules (no-op). This function models rulesets seen in the field, so we
751 * can confirm that osmo-upf ACKs and ignores. */
752private function f_ruleset_noop() return PFCP_Ruleset
753{
754 var PFCP_Ruleset r := { {}, {} };
755 var integer pdr_id := lengthof(r.pdr) + 1;
756 var integer far_id := lengthof(r.far) + 1;
757
758 r.pdr := r.pdr & {
759 valueof(
760 ts_PFCP_Create_PDR(
761 pdr_id,
762 ts_PFCP_PDI(
763 CP_FUNCTION,
764 local_F_TEID := ts_PFCP_F_TEID_choose_v4('17'O)),
765 ts_PFCP_Outer_Header_Removal(GTP_U_UDP_IPV4),
766 far_id
767 )
768 )
769 };
770 r.far := r.far & {
771 valueof(
772 ts_PFCP_Create_FAR(
773 far_id,
774 ts_PFCP_Apply_Action_FORW(),
775 valueof(ts_PFCP_Forwarding_Parameters(ACCESS))
776 )
777 )
778 };
779
780 /* And another one (sic) */
781 pdr_id := lengthof(r.pdr) + 1;
782 far_id := lengthof(r.far) + 1;
783
784 r.pdr := r.pdr & {
785 valueof(
786 ts_PFCP_Create_PDR(
787 pdr_id,
788 ts_PFCP_PDI(
789 CP_FUNCTION,
790 local_F_TEID := ts_PFCP_F_TEID_choose_v4('2a'O)),
791 far_id := far_id
792 )
793 )
794 };
795 r.far := r.far & {
796 valueof(
797 ts_PFCP_Create_FAR(
798 far_id,
799 ts_PFCP_Apply_Action_FORW(),
800 valueof(ts_PFCP_Forwarding_Parameters(ACCESS))
801 )
802 )
803 };
804 return r;
805}
806
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100807/* Return a rule set that does GTP encapsulation and decapsulation, in both directions. */
808private function f_ruleset_tunend(GTP_Action gtp, charstring netinst_access := "access") return PFCP_Ruleset
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200809{
810 var PFCP_Ruleset rules := { {}, {} };
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100811 f_ruleset_add_GTP_decaps(rules,
812 pdr_id := gtp.access.pdr_id,
813 src_netinst := netinst_access,
814 far_id := gtp.core.tunend.far_id);
815 f_ruleset_add_GTP_encaps(rules,
816 gtp.core.tunend.pdr_id,
817 gtp.core.tunend.ip_l,
818 gtp.access.far_id,
819 gtp.access.teid_r,
820 f_inet_addr(gtp.access.gtp_ip_r));
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200821 return rules;
822}
823
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100824/* Return a rule set that does GTP tunnel forwarding in both directions.
825 * If core_gtp_known == true, place full FORW rules in both directions.
826 * If core_gtp_known == false, keep the Core side as DROP: this allows testing the usual/realistic case, where upon
827 * Session Establishment, the core side PGW has not yet provided the destination GTP F-TEID, which will follow later in
828 * a Session Modification. (This test suite has already configured which GTP F-TEID will be used on the core side, but
829 * we're omitting it from Session Establishment, until it is time to use it in f_session_mod()). */
830private function f_ruleset_tunmap(GTP_Action gtp, boolean core_gtp_known := true,
831 charstring netinst_access := "access",
832 charstring netinst_core := "core") return PFCP_Ruleset
833{
834 var PFCP_Ruleset rules := { {}, {} };
835 /* Access to Core */
836 if (core_gtp_known) {
837 f_ruleset_add_GTP_forw(rules,
838 pdr_id := gtp.access.pdr_id,
839 src_iface := ACCESS,
840 src_netinst := netinst_access,
841 far_id := gtp.core.tunmap.far_id,
842 dest_iface := CORE,
843 dest_remote_f_teid := valueof(ts_PFCP_F_TEID_ipv4(gtp.core.tunmap.teid_r,
844 f_inet_addr(gtp.core.tunmap.gtp_ip_r))));
845 } else {
846 /* The Core remote GTP will follow in a Session Modification, for now set Core->Access to DROP */
847 f_ruleset_add_GTP_drop(rules,
848 pdr_id := gtp.access.pdr_id,
849 src_iface := ACCESS,
850 src_netinst := netinst_access,
851 far_id := gtp.core.tunmap.far_id,
852 dest_iface := CORE);
853 }
854 /* Core to Access */
855 f_ruleset_add_GTP_forw(rules,
856 pdr_id := gtp.core.tunmap.pdr_id,
857 src_iface := CORE,
858 src_netinst := netinst_core,
859 far_id := gtp.access.far_id,
860 dest_iface := ACCESS,
861 dest_remote_f_teid := valueof(ts_PFCP_F_TEID_ipv4(gtp.access.teid_r,
862 f_inet_addr(gtp.access.gtp_ip_r))));
863 return rules;
864}
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200865
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100866/* From a PFCP Session Establishment Response, retrieve the F_TEID returned in the Created PDR IE for the given PDR Id
867 */
868private function f_get_created_local_f_teid(PDU_PFCP sess_est_resp, integer pdr_id) return F_TEID
869{
870 for (var integer i := 0;
871 i < lengthof(sess_est_resp.message_body.pfcp_session_establishment_response.created_PDR_list);
872 i := i + 1) {
873 var Created_PDR cpdr := sess_est_resp.message_body.pfcp_session_establishment_response.created_PDR_list[i];
874 if (oct2int(cpdr.grouped_ie.pdr_id.rule_id) != pdr_id) {
875 continue;
876 }
877 log("osmo-upf has chosen local F-TEID: PDR-" & int2str(pdr_id) & " = ", cpdr.grouped_ie.local_F_TEID);
878 return cpdr.grouped_ie.local_F_TEID;
879 }
880 setverdict(fail, "PDR Id " & int2str(pdr_id) & " not found in PFCP message");
881 mtc.stop;
882}
883
884/* Run a PFCP Session Establishment procedure */
885private function f_session_est(inout PFCP_session s, PFCP_Ruleset rules) runs on CPF_ConnHdlr
886{
887 log("f_session_est: rules = ", rules);
Neels Hofmeyr1e311462023-01-11 01:19:12 +0100888 PFCP.send(ts_PFCP_Session_Est_Req(ts_PFCP_Node_ID_ipv4(f_inet_addr(g_pars.local_addr)),
889 ts_PFCP_F_SEID_ipv4(f_inet_addr(g_pars.local_addr), s.cp_seid),
890 rules.pdr, rules.far));
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200891
892 var PDU_PFCP pfcp;
893 PFCP.receive(tr_PFCP_Session_Est_Resp(s.cp_seid)) -> value pfcp;
894 s.up_seid := pfcp.message_body.pfcp_session_establishment_response.UP_F_SEID.seid;
895 s.gtp.seid_l := s.up_seid;
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100896
897 var F_TEID access_local_f_teid := f_get_created_local_f_teid(pfcp, s.gtp.access.pdr_id);
898 s.gtp.access.gtp_ip_l := f_inet_ntoa(access_local_f_teid.ipv4_address);
899 s.gtp.access.teid_l := access_local_f_teid.teid;
900
901 if (ischosen(s.gtp.core.tunmap)) {
902 var F_TEID core_local_f_teid := f_get_created_local_f_teid(pfcp, s.gtp.core.tunmap.pdr_id);
903 s.gtp.core.tunmap.gtp_ip_l := f_inet_ntoa(core_local_f_teid.ipv4_address);
904 s.gtp.core.tunmap.teid_l := core_local_f_teid.teid;
905 }
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200906 log("established PFCP session: ", s);
907}
908
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100909/* Run a PFCP Session Modification procedure */
910private function f_session_mod(inout PFCP_session s) runs on CPF_ConnHdlr
911{
912 PFCP.send(ts_PFCP_Session_Mod_Req(s.up_seid, f_tunmap_upd_far_to_core(s.gtp)));
913 PFCP.receive(tr_PFCP_Session_Mod_Resp(s.cp_seid));
914 log("modified PFCP session: ", s);
915}
916
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +0100917private function f_create_PFCP_session_tunend() runs on CPF_ConnHdlr return PFCP_session
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200918{
919 var PFCP_session s := {
920 up_seid := -,
921 cp_seid := f_next_seid(),
922 gtp := {
Neels Hofmeyr366cdc72022-11-21 16:28:00 +0100923 kind := "tunend",
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100924 access := {
925 pdr_id := 1,
926 /* gtp_ip_l and teid_l will be returned by Session Establishment Response, Created PDR */
927 gtp_ip_l := "",
928 teid_l := '00000000'O,
929 far_id := 2,
930 gtp_ip_r := "127.0.0.2",
931 teid_r := f_next_remote_teid()
932 },
933 core := {
934 tunend := {
935 pdr_id := 2,
936 ip_l := f_next_ue_addr(),
937 far_id := 1
938 }
939 },
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200940 pfcp_peer := g_pars.local_addr,
941 seid_l := '0000000000000000'O
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +0100942 /* seid_l will be returned by Session Establishment Response */
943 }
944 };
945 return s;
946}
947
948private function f_create_PFCP_session_tunmap() runs on CPF_ConnHdlr return PFCP_session
949{
950 var PFCP_session s := {
951 up_seid := -,
952 cp_seid := f_next_seid(),
953 gtp := {
954 kind := "tunmap",
955 access := {
956 pdr_id := 1,
957 /* gtp_ip_l and teid_l will be returned by Session Establishment Response, Created PDR */
958 gtp_ip_l := "",
959 teid_l := '00000000'O,
960 far_id := 2,
961 gtp_ip_r := "127.0.0.2",
962 teid_r := f_next_remote_teid()
963 },
964 core := {
965 tunmap := {
966 pdr_id := 2,
967 /* gtp_ip_l and teid_l will be returned by Session Establishment Response, Created PDR */
968 gtp_ip_l := "",
969 teid_l := '00000000'O,
970 far_id := 1,
971 gtp_ip_r := "127.0.0.3",
972 teid_r := f_next_remote_teid()
973 }
974 },
975 pfcp_peer := g_pars.local_addr,
976 seid_l := '0000000000000000'O
977 /* seid_l will be returned by Session Establishment Response */
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200978 }
979 };
980 return s;
981}
982
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +0100983/* Do a PFCP Session Establishment with default values (see f_create_PFCP_session_tunend()) */
984private function f_session_est_tunend() runs on CPF_ConnHdlr return PFCP_session
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200985{
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +0100986 var PFCP_session s := f_create_PFCP_session_tunend();
Neels Hofmeyr366cdc72022-11-21 16:28:00 +0100987 f_session_est(s, f_ruleset_tunend(s.gtp));
Neels Hofmeyr2d292742022-06-07 23:58:05 +0200988 return s;
989}
990
991private function f_session_del(PFCP_session s) runs on CPF_ConnHdlr {
992 PFCP.send(ts_PFCP_Session_Del_Req(s.up_seid));
993 PFCP.receive(tr_PFCP_Session_Del_Resp(s.cp_seid));
994}
995
996private function f_tc_assoc(charstring id) runs on CPF_ConnHdlr {
997 f_assoc_setup();
998 f_assoc_release();
999 setverdict(pass);
1000}
1001
1002/* Verify that the CPF can send a Node-ID of the IPv4 type */
1003testcase TC_assoc_node_id_v4() runs on test_CT {
1004 var CPF_ConnHdlr vc_conn;
1005
1006 f_init(guard_timeout := 5.0);
1007 vc_conn := f_start_handler(refers(f_tc_assoc));
1008 vc_conn.done;
1009 f_shutdown_helper();
1010}
1011
1012/* Verify that the CPF can send a Node-ID of the FQDN type */
1013testcase TC_assoc_node_id_fqdn() runs on test_CT {
1014 var CPF_ConnHdlr vc_conn;
1015 var TestHdlrParams pars := f_gen_test_hdlr_pars();
1016
1017 pars.local_node_id := valueof(ts_PFCP_Node_ID_fqdn("\7example\3com"));
1018
1019 f_init(guard_timeout := 5.0);
1020 vc_conn := f_start_handler(refers(f_tc_assoc), pars);
1021 vc_conn.done;
1022 f_shutdown_helper();
1023}
1024
1025/* Verify PFCP Session Establishment and Deletion */
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001026private function f_tc_session_est_tunend(charstring id) runs on CPF_ConnHdlr {
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001027 f_assoc_setup();
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001028 var PFCP_session s := f_session_est_tunend();
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001029 f_sleep(1.0);
1030 f_vty_expect_session_active(UPFVTY, s);
1031 f_session_del(s);
1032 f_vty_expect_no_active_sessions(UPFVTY);
1033 f_vty_expect_no_gtp_actions(UPFVTY);
1034 f_assoc_release();
1035 setverdict(pass);
1036}
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001037testcase TC_session_est_tunend() runs on test_CT {
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001038 var CPF_ConnHdlr vc_conn;
1039
1040 f_init(guard_timeout := 15.0);
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001041 vc_conn := f_start_handler(refers(f_tc_session_est_tunend));
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001042 vc_conn.done;
1043 f_shutdown_helper();
1044}
1045
1046/* Verify that releasing a PFCP Association also releases all its sessions and GTP actions. */
1047private function f_tc_session_term_by_assoc_rel(charstring id) runs on CPF_ConnHdlr {
1048 f_assoc_setup();
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001049 var PFCP_session s := f_session_est_tunend();
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001050 f_sleep(1.0);
1051 f_vty_expect_session_active(UPFVTY, s);
1052 f_assoc_release();
1053 f_vty_expect_no_active_sessions(UPFVTY);
1054 f_vty_expect_no_gtp_actions(UPFVTY);
1055 setverdict(pass);
1056}
1057testcase TC_session_term_by_assoc_rel() runs on test_CT {
1058 var CPF_ConnHdlr vc_conn;
1059
1060 f_init(guard_timeout := 15.0);
1061 vc_conn := f_start_handler(refers(f_tc_session_term_by_assoc_rel));
1062 vc_conn.done;
1063 f_shutdown_helper();
1064}
1065
1066/* Verify that PFCP Sessions with a src-interface other than ACCESS or CORE are ACKed by osmo-upf but have no effect. */
1067private function f_tc_session_est_noop(charstring id) runs on CPF_ConnHdlr {
1068 f_assoc_setup();
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001069 var PFCP_session s := f_create_PFCP_session_tunend();
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001070 f_session_est(s, f_ruleset_noop());
1071
1072 f_sleep(1.0);
1073 f_vty_expect_session_status(UPFVTY, s, PFCP_session_inactive);
1074
1075 f_session_del(s);
1076 f_vty_expect_no_active_sessions(UPFVTY);
1077 f_vty_expect_no_gtp_actions(UPFVTY);
1078 f_assoc_release();
1079 setverdict(pass);
1080}
1081testcase TC_session_est_noop() runs on test_CT {
1082 var CPF_ConnHdlr vc_conn;
1083
1084 f_init(guard_timeout := 15.0);
1085 vc_conn := f_start_handler(refers(f_tc_session_est_noop));
1086 vc_conn.done;
1087 f_shutdown_helper();
1088}
1089
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +01001090/* Verify that the Network Instance IE in Create PDR chooses the right local address for a tunmap session */
1091private function f_tc_session_est_tunmap(charstring id) runs on CPF_ConnHdlr {
1092 f_assoc_setup();
1093 var PFCP_session s := f_create_PFCP_session_tunmap();
1094 f_session_est(s, f_ruleset_tunmap(s.gtp));
1095 f_sleep(1.0);
1096 f_vty_expect_session_active(UPFVTY, s);
1097 f_session_del(s);
1098 f_vty_expect_no_active_sessions(UPFVTY);
1099 f_vty_expect_no_gtp_actions(UPFVTY);
1100 f_assoc_release();
1101 setverdict(pass);
1102}
1103testcase TC_session_est_tunmap() runs on test_CT {
1104 var CPF_ConnHdlr vc_conn;
1105
1106 f_init(guard_timeout := 15.0);
1107
1108 vc_conn := f_start_handler(refers(f_tc_session_est_tunmap));
1109 vc_conn.done;
1110 f_shutdown_helper();
1111}
1112
1113/* Set up a tunmap session with a partial Session Establishment, followed by a Session Modification to complete it. */
1114private function f_session_est_mod_tunmap(charstring netinst_access, charstring expect_gtp_ip_access,
1115 charstring netinst_core, charstring expect_gtp_ip_core) runs on CPF_ConnHdlr {
1116 f_assoc_setup();
1117 var PFCP_session s := f_create_PFCP_session_tunmap();
1118 f_session_est(s, f_ruleset_tunmap(s.gtp, core_gtp_known := false,
1119 netinst_access := netinst_access, netinst_core := netinst_core));
1120 /* The locally chosen GTP IP addresses where osmo-upf receives GTP traffic were chosen by netinst_access /
1121 * netinst_core and are returned in s.gtp.access.gtp_ip_l / s.gtp.core.tunmap.gtp_ip_l. Verify that the netinst
1122 * names have returned their matching IP addresses. */
1123 if (s.gtp.access.gtp_ip_l != expect_gtp_ip_access) {
1124 setverdict(fail, "Network Instance '" & netinst_access & "' should have yielded GTP IP " &
1125 expect_gtp_ip_access & " but osmo-upf chose " & s.gtp.access.gtp_ip_l);
1126 mtc.stop;
1127 }
1128 if (s.gtp.core.tunmap.gtp_ip_l != expect_gtp_ip_core) {
1129 setverdict(fail, "Network Instance '" & netinst_core & "' should have yielded GTP IP " &
1130 expect_gtp_ip_core & " but osmo-upf chose " & s.gtp.core.tunmap.gtp_ip_l);
1131 mtc.stop;
1132 }
1133
1134 f_sleep(1.0);
1135 f_vty_expect_session_status(UPFVTY, s, PFCP_session_inactive);
1136
1137 f_session_mod(s);
1138 f_sleep(1.0);
1139 f_vty_expect_session_active(UPFVTY, s);
1140 f_session_del(s);
1141 f_vty_expect_no_active_sessions(UPFVTY);
1142 f_vty_expect_no_gtp_actions(UPFVTY);
1143 f_assoc_release();
1144 setverdict(pass);
1145}
1146/* Run f_session_est_mod_tunmap() with the first Network Instances */
1147private function f_tc_session_est_mod_tunmap(charstring id) runs on CPF_ConnHdlr {
1148 f_session_est_mod_tunmap("access", mp_netinst_access_ip_1, "core", mp_netinst_core_ip_1);
1149}
1150/* Run f_session_est_mod_tunmap() with the second Network Instances */
1151private function f_tc_session_est_mod_tunmap2(charstring id) runs on CPF_ConnHdlr {
1152 f_session_est_mod_tunmap("access2", mp_netinst_access_ip_2, "core2", mp_netinst_core_ip_2);
1153}
1154testcase TC_session_est_mod_tunmap() runs on test_CT {
1155 var CPF_ConnHdlr vc_conn;
1156
1157 f_init(guard_timeout := 15.0);
1158
1159 vc_conn := f_start_handler(refers(f_tc_session_est_mod_tunmap));
1160 vc_conn.done;
1161 f_shutdown_helper();
1162}
1163testcase TC_session_est_mod_tunmap2() runs on test_CT {
1164 var CPF_ConnHdlr vc_conn;
1165
1166 f_init(guard_timeout := 15.0);
1167
1168 vc_conn := f_start_handler(refers(f_tc_session_est_mod_tunmap2));
1169 vc_conn.done;
1170 f_shutdown_helper();
1171}
1172
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001173control {
1174 execute( TC_assoc_node_id_v4() );
1175 execute( TC_assoc_node_id_fqdn() );
Neels Hofmeyrb1dc8262022-11-26 01:08:53 +01001176 execute( TC_session_est_tunend() );
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001177 execute( TC_session_term_by_assoc_rel() );
1178 execute( TC_session_est_noop() );
Neels Hofmeyrcf4935a2022-11-21 17:15:46 +01001179 execute( TC_session_est_tunmap() );
1180 execute( TC_session_est_mod_tunmap() );
1181 execute( TC_session_est_mod_tunmap2() );
Neels Hofmeyr2d292742022-06-07 23:58:05 +02001182}
1183
1184}