blob: 22e32da6f1bd43b168cefa0127a1b9c4c418b577 [file] [log] [blame]
Harald Welte4526da92020-03-05 23:08:10 +01001module PGW_Tests {
2
3import from General_Types all;
4import from Osmocom_Types all;
5import from Native_Functions all;
6
7import from GTPv2_Types all;
8import from GTPv2_Templates all;
9import from GTPv2_Emulation all;
10
11import from UECUPS_Types all;
12
13import from DNS_Helpers all;
14
15modulepar {
16 charstring mp_pgw_hostname := "127.0.0.3";
17 charstring mp_local_hostname_c := "127.0.0.1";
18 charstring mp_local_hostname_u := "127.0.0.1";
19 charstring mp_run_prog_as_user := "laforge";
20 charstring mp_ping_hostname := "10.45.0.1";
21}
22
23/* main component, we typically have one per testcase */
24type component PGW_Test_CT {
25 var GTPv2_Emulation_CT vc_GTP2;
26 port GTP2EM_PT TEID0;
27}
28
29/* per-session component; we typically have 1..N per testcase */
30type component PGW_Session_CT extends GTP2_ConnHdlr {
31 var SessionPars g_pars;
32
33 /* TEI (Data) local side */
34 var OCT4 g_teid;
35 /* TEI (Control) local side */
36 var OCT4 g_teic;
37 /* TEI (Data) remote side */
38 var OCT4 g_teid_remote;
39 /* TEI (Control) remote side */
40 var OCT4 g_teic_remote;
41 /* GTP-U IPv4 address remote sie */
42 var OCT4 g_gtpu4_remote;
43 var OCT16 g_gtpu6_remote;
44
45 /* Address allocation */
46 var OCT4 g_ip4_addr;
47 var OCT16 g_ip6_addr;
48 var integer g_ip6_plen;
49}
50
51/* configuration data for a given Session */
52type record SessionPars {
53 hexstring imsi,
54 octetstring msisdn optional,
55 // serving network
56 integer rat_type,
57 // flags?
58 charstring apn,
59 /* Apn subscribed or non-subscribed */
60 boolean selection_mode,
61 BIT3 pdn_type,
62 /* PAA */
63 /* Max APN Restriction */
64 /* APN-AMBR */
65 octetstring pco optional,
66 octetstring epco optional,
67 /* Bearer Contexts to be created */
68
69 charstring tun_dev_name,
70 charstring tun_netns_name optional
71}
72
73template (value) SessionPars
74t_SessionPars(hexstring imsi, charstring tundev, integer rat_type := 6, charstring apn := "internet",
75 boolean selection_mode := false, BIT3 pdn_type := '001'B) := {
76 imsi := imsi,
77 msisdn := omit,
78 rat_type := rat_type,
79 apn := apn,
80 selection_mode := selection_mode,
81 pdn_type := pdn_type,
82 pco := omit,
83 epco := omit,
84 tun_dev_name := tundev,
85 tun_netns_name := tundev
86}
87
88type record BearerConfig {
89 integer eps_bearer_id
90}
91
92type function void_fn() runs on PGW_Session_CT;
93
94private function f_init() runs on PGW_Test_CT {
95 var Gtp2EmulationCfg cfg := {
96 gtpc_bind_ip := mp_local_hostname_c,
97 gtpc_bind_port := GTP2C_PORT,
98 gtpc_remote_ip := mp_pgw_hostname,
99 gtpc_remote_port := GTP2C_PORT,
100 sgw_role := true,
101 use_gtpu_daemon := true
102 };
103
104 vc_GTP2 := GTPv2_Emulation_CT.create("GTP2_EM");
105 map(vc_GTP2:GTP2C, system:GTP2C);
106 connect(vc_GTP2:TEID0, self:TEID0);
107 vc_GTP2.start(GTPv2_Emulation.main(cfg));
108}
109
110function f_start_handler(void_fn fn, template (omit) SessionPars pars := omit)
111runs on PGW_Test_CT return PGW_Session_CT {
112 var charstring id := testcasename();
113 var PGW_Session_CT vc_conn;
114 vc_conn := PGW_Session_CT.create(id);
115 connect(vc_conn:GTP2, vc_GTP2:CLIENT);
116 connect(vc_conn:GTP2_PROC, vc_GTP2:CLIENT_PROC);
117 vc_conn.start(f_handler_init(fn, pars));
118 return vc_conn;
119}
120
121private function f_handler_init(void_fn fn, template (omit) SessionPars pars := omit)
122runs on PGW_Session_CT {
123 if (isvalue(pars)) {
124 g_pars := valueof(pars);
125 }
126 fn.apply();
127}
128
129
130/* find TEID of given interface type (and optionally instance) */
131private function f_find_teid(FullyQualifiedTEID_List list,
132 template (present) integer if_type,
133 template (present) BIT4 instance := ?)
134return template (omit) FullyQualifiedTEID
135{
136 var integer i;
137 for (i := 0; i < lengthof(list); i := i+1) {
138 if (match(list[i].interfaceType, if_type) and
139 match(list[i].instance, instance)) {
140 return list[i];
141 }
142 }
143 return omit;
144}
145
146/* process one to-be-created bearer context */
147private function process_bctx_create(BearerContextGrouped bctx) runs on PGW_Session_CT
148{
149 /* FIXME: EPS Bearer ID */
150 /* FIXME: Cause */
151
152 /* find F-TEID of the P-GW U side */
153 var FullyQualifiedTEID rx_fteid;
154 rx_fteid := valueof(f_find_teid(bctx.bearerContextIEs.fullyQualifiedTEID, 5, '0010'B));
155 g_teid_remote := rx_fteid.tEID_GRE_Key;
156 if (rx_fteid.v4_Flag == '1'B) {
157 g_gtpu4_remote := rx_fteid.iPv4_Address;
158 }
159 if (rx_fteid.v6_Flag == '1'B) {
160 g_gtpu6_remote := rx_fteid.iPv6_Address;
161 }
162
163 var UECUPS_CreateTun uecups_create := {
164 tx_teid := oct2int(g_teid_remote),
165 rx_teid := oct2int(g_teid),
166 user_addr_type := IPV4,
167 user_addr := '00000000'O,
168 local_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(mp_local_hostname_u))),
169 remote_gtp_ep := valueof(ts_UECUPS_SockAddr(g_gtpu4_remote)),
170 tun_dev_name := g_pars.tun_dev_name,
171 tun_netns_name := g_pars.tun_netns_name
172 };
173
174 /* create tunnel in daemon */
175 if (isbound(g_ip4_addr)) {
176 uecups_create.user_addr := g_ip4_addr;
177 f_gtp2_create_tunnel(uecups_create);
178 }
179 if (isbound(g_ip6_addr)) {
180 uecups_create.user_addr_type := IPV6;
181 uecups_create.user_addr := g_ip6_addr;
182 f_gtp2_create_tunnel(uecups_create);
183 }
184}
185
186/* create a session on the PGW */
187private function f_create_session() runs on PGW_Session_CT {
188 var PDU_GTPCv2 rx;
189
190 /* allocate + register TEID-C on local side */
191 g_teic := f_gtp2_allocate_teid();
192 g_teid := g_teic;
193
194 var template (value) FullyQualifiedTEID fteid_c_ie, fteid_u_ie;
195 fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPC, g_teic, 0,
196 f_inet_addr(mp_local_hostname_c), omit);
197 fteid_u_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPU, g_teid, 2,
198 f_inet_addr(mp_local_hostname_u), omit);
199 var template (value) PDU_GTPCv2 g2c :=
200 ts_GTP2C_CreateSessionReq(imsi := g_pars.imsi, msisdn := omit, rat_type := 6,
201 sender_fteid := fteid_c_ie,
202 apn := f_enc_dns_hostname(g_pars.apn),
203 pdn_type := g_pars.pdn_type, teid_list := { fteid_u_ie },
204 chg_car := '0000'O, bearer_id := 1);
205 /* open5gs up to 1.2.3 won't accept it without ULI, despite not mandatory */
206 var template (value) TAI tai := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0001'O };
207 var template (value) ECGI ecgi := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0'H, 23 };
208 g2c.gtpcv2_pdu.createSessionRequest.userLocationInfo := ts_GTP2C_UserLocInfo(tai := tai, ecgi := ecgi);
209
210 GTP2.send(g2c);
211 alt {
212 [] GTP2.receive(tr_GTP2C_CreateSessionResp(d_teid:=g_teic, cause:='10'O)) -> value rx {
213 /* extract TEIDs */
214 var CreateSessionResponse resp := rx.gtpcv2_pdu.createSessionResponse;
215 g_teic_remote := resp.fullyQualifiedTEID[0].tEID_GRE_Key;
216
217 /* extract allocated address[es] */
218 var PDN_Address_and_Prefix paa := resp.pDN_AddressAllocation.pDN_Address_and_Prefix;
219 if (ischosen(paa.iPv4_Address)) {
220 g_ip4_addr := paa.iPv4_Address;
221 } else if (ischosen(paa.iPv6_Address)) {
222 g_ip6_addr := paa.iPv6_Address.iPv6_Address;
223 g_ip6_plen := paa.iPv6_Address.prefixLength;
224 } else if (ischosen(paa.iPv4_IPv6)) {
225 g_ip4_addr := paa.iPv4_IPv6.iPv4_Address;
226 g_ip6_addr := paa.iPv4_IPv6.iPv6_Address;
227 g_ip6_plen := paa.iPv4_IPv6.prefixLength;
228 }
229 var integer i;
230 for (i := 0; i < lengthof(resp.bearerContextGrouped); i := i+1) {
231 var BearerContextGrouped bctx := resp.bearerContextGrouped[i];
232 select (bctx.instance) {
233 case ('0000'B) { // created
234 process_bctx_create(bctx);
235 }
236 case ('0001'B) { // removed
237 setverdict(fail, "We don't expect removed bearer contexts yet");
238 }
239 }
240 }
241 }
242 [] GTP2.receive(tr_GTP2C_CreateSessionResp(d_teid:=g_teic, cause:=?)) -> value rx {
243 setverdict(fail, "Unexpected CreateSessionResp(cause=",
244 rx.gtpcv2_pdu.createSessionResponse.cause.causeValue, ")");
245 }
246 [] GTP2.receive {
247 setverdict(fail, "Unexpected GTPv2 while waiting for CreateSessionResp");
248 }
249 }
250
251}
252
253/* delete the session from the PGW */
254private function f_delete_session(template (omit) OCT1 tx_cause := omit,
255 template (present) OCT4 exp_teid,
256 template (present) OCT1 exp_cause) runs on PGW_Session_CT {
257 var template (value) FullyQualifiedTEID fteid_c_ie
258 fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPC, g_teic, 0,
259 f_inet_addr(mp_local_hostname_c), omit);
260 var template PDU_GTPCv2 g2c :=
261 ts_GTP2C_DeleteSessionReq(d_teid := g_teic_remote, cause := tx_cause,
262 sender_fteid := fteid_c_ie,
263 teid_list := {}, bearer_id := 1);
264
265 GTP2.send(g2c);
266 alt {
267 [] GTP2.receive(tr_GTP2C_DeleteSessionResp(d_teid := exp_teid, cause := exp_cause)) {
268 setverdict(pass);
269 }
270 [] GTP2.receive(tr_GTP2C_DeleteSessionResp(?, ?)) {
271 setverdict(fail, "Unexpected DeleteSessionResp");
272 }
273 [] GTP2.receive {
274 setverdict(fail, "Unexpected GTPv2 while waiting for DeleteSessionResp");
275 }
276 }
277
278 /* destroy tunnel in daemon */
279 if (isbound(g_teid)) {
280 var UECUPS_DestroyTun uecups_destroy := {
281 local_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(mp_local_hostname_u))),
282 rx_teid := oct2int(g_teid)
283 };
284 /* FIXME: what about IPv4/IPv6 differentiation? */
285 f_gtp2_destroy_tunnel(uecups_destroy);
286 }
287}
288
289/* start a program on the user plane side; return its PID */
290private function f_start_prog(charstring command) runs on PGW_Session_CT return integer
291{
292 var UECUPS_StartProgram sprog := {
293 command := command,
294 environment := {},
295 run_as_user := mp_run_prog_as_user,
296 tun_netns_name := g_pars.tun_netns_name
297 };
298 var UECUPS_StartProgramRes res := f_gtp2_start_program(sprog);
299 if (res.result != OK) {
300 setverdict(fail, "Unable to start program '", command, "'");
301 }
302 return res.pid;
303}
304
305/* wait for termination of a given PID with specified exit_code */
306private function f_wait_term(integer pid, template (present) integer exit_code := 0,
307 float tout := 10.0) runs on PGW_Session_CT
308{
309 timer T := tout;
310
311 T.start;
312 alt {
313 [] GTP2.receive(UECUPS_ProgramTermInd:{pid := pid, exit_code := exit_code}) {
314 setverdict(pass);
315 }
316 [] GTP2.receive(UECUPS_ProgramTermInd:?) {
317 setverdict(fail, "Received unexpected ProgramTermInd");
318 }
319 [] T.timeout {
320 setverdict(fail, "timeout waiting for user-plane program termination");
321 }
322 }
323}
324
325/* execute a program and wait for result */
326private function f_start_prog_wait(charstring command, template integer exit_code := 0, float tout := 10.0) runs on PGW_Session_CT
327{
328 var integer pid := f_start_prog(command);
329 f_wait_term(pid, exit_code, tout);
330}
331
332/* execute ping command and wait for result */
333private function f_ping4(charstring host, integer interval := 1, integer count := 10) runs on PGW_Session_CT
334{
335 var charstring ping :="ping -c " & int2str(count) & " -i " & int2str(interval);
336 ping := ping & " -I " & f_inet_ntoa(g_ip4_addr);
337 ping := ping & " " & host;
338 f_start_prog_wait(ping);
339}
340
341
342
343
344/* send echo request; expect response */
345testcase TC_tx_echo() runs on PGW_Test_CT {
346 timer T := 5.0;
347
348 f_init();
349
350 TEID0.send(ts_GTP2C_EchoReq(0));
351 T.start;
352 alt {
353 [] TEID0.receive(tr_GTP2C_EchoResp) {
354 setverdict(pass);
355 }
356 [] T.timeout {
357 setverdict(fail, "timeout waiting for Echo Response");
358 }
359 }
360}
361
362/* create a session, expect it to succeed */
363private function f_TC_createSession() runs on PGW_Session_CT {
364 f_create_session();
365 setverdict(pass);
366}
367testcase TC_createSession() runs on PGW_Test_CT {
368 var PGW_Session_CT vc_conn;
369 var SessionPars pars := valueof(t_SessionPars('001010123456789'H, "tun22"));
370 f_init();
371 vc_conn := f_start_handler(refers(f_TC_createSession), pars);
372 vc_conn.done;
373}
374
375/* create a session, then execute a ping command on the user plane */
376private function f_TC_createSession_ping4() runs on PGW_Session_CT {
377 f_create_session();
378 f_ping4(mp_ping_hostname);
379 setverdict(pass);
380}
381testcase TC_createSession_ping4() runs on PGW_Test_CT {
382 var PGW_Session_CT vc_conn;
383 var SessionPars pars := valueof(t_SessionPars('001010123456789'H, "tun23"));
384 f_init();
385 vc_conn := f_start_handler(refers(f_TC_createSession_ping4), pars);
386 vc_conn.done;
387}
388
389/* create a session, then delete it again */
390private function f_TC_createSession_deleteSession() runs on PGW_Session_CT {
391 f_create_session();
392 f_delete_session(omit, g_teic, '10'O);
393 setverdict(pass);
394}
395testcase TC_createSession_deleteSession() runs on PGW_Test_CT {
396 var PGW_Session_CT vc_conn;
397 var SessionPars pars := valueof(t_SessionPars('001010123456789'H, "tun23"));
398 f_init();
399 vc_conn := f_start_handler(refers(f_TC_createSession_deleteSession), pars);
400 vc_conn.done;
401}
402
403/* send a DeleteSessionReq for an unknown/invalid TEID */
404private function f_TC_deleteSession_unknown() runs on PGW_Session_CT {
405 g_teic := f_gtp2_allocate_teid();
406 g_teic_remote := f_rnd_octstring(4);
407 f_delete_session(omit, '00000000'O, '40'O /* Context Unknown */);
408 setverdict(pass);
409}
410testcase TC_deleteSession_unknown() runs on PGW_Test_CT {
411 var PGW_Session_CT vc_conn;
412 var SessionPars pars := valueof(t_SessionPars('001010123456789'H, "tun23"));
413 f_init();
414 vc_conn := f_start_handler(refers(f_TC_deleteSession_unknown), pars);
415 vc_conn.done;
416}
417
418
419
420
421control {
422 execute( TC_tx_echo() );
423 execute( TC_createSession() );
424 execute( TC_createSession_ping4() );
425 execute( TC_createSession_deleteSession() );
426 execute( TC_deleteSession_unknown() );
427}
428
429
430}