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