blob: c99302c02997d2dfe054e769f11ff9090aba3321 [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
Harald Weltefe595e42020-04-21 22:56:47 +0200129private function f_concat_pad(integer tot_len, hexstring prefix, integer suffix) return hexstring {
130 var integer suffix_len := tot_len - lengthof(prefix);
131 var charstring suffix_ch := int2str(suffix);
132 var integer pad_len := suffix_len - lengthof(suffix_ch);
133
134 return prefix & int2hex(0, pad_len) & str2hex(suffix_ch);
135}
136
137function f_gen_imei(integer suffix) return hexstring {
138 return f_concat_pad(14, '49999'H, suffix);
139}
140
141function f_gen_imsi(integer suffix) return hexstring {
142 return f_concat_pad(15, '26242'H, suffix);
143}
144
145function f_gen_msisdn(integer suffix) return hexstring {
146 return f_concat_pad(12, '49123'H, suffix);
147}
148
Harald Welte4526da92020-03-05 23:08:10 +0100149
150/* find TEID of given interface type (and optionally instance) */
151private function f_find_teid(FullyQualifiedTEID_List list,
152 template (present) integer if_type,
153 template (present) BIT4 instance := ?)
154return template (omit) FullyQualifiedTEID
155{
156 var integer i;
157 for (i := 0; i < lengthof(list); i := i+1) {
158 if (match(list[i].interfaceType, if_type) and
159 match(list[i].instance, instance)) {
160 return list[i];
161 }
162 }
163 return omit;
164}
165
166/* process one to-be-created bearer context */
167private function process_bctx_create(BearerContextGrouped bctx) runs on PGW_Session_CT
168{
169 /* FIXME: EPS Bearer ID */
170 /* FIXME: Cause */
171
172 /* find F-TEID of the P-GW U side */
173 var FullyQualifiedTEID rx_fteid;
174 rx_fteid := valueof(f_find_teid(bctx.bearerContextIEs.fullyQualifiedTEID, 5, '0010'B));
175 g_teid_remote := rx_fteid.tEID_GRE_Key;
176 if (rx_fteid.v4_Flag == '1'B) {
177 g_gtpu4_remote := rx_fteid.iPv4_Address;
178 }
179 if (rx_fteid.v6_Flag == '1'B) {
180 g_gtpu6_remote := rx_fteid.iPv6_Address;
181 }
182
183 var UECUPS_CreateTun uecups_create := {
184 tx_teid := oct2int(g_teid_remote),
185 rx_teid := oct2int(g_teid),
186 user_addr_type := IPV4,
187 user_addr := '00000000'O,
188 local_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(mp_local_hostname_u))),
189 remote_gtp_ep := valueof(ts_UECUPS_SockAddr(g_gtpu4_remote)),
190 tun_dev_name := g_pars.tun_dev_name,
191 tun_netns_name := g_pars.tun_netns_name
192 };
193
194 /* create tunnel in daemon */
195 if (isbound(g_ip4_addr)) {
196 uecups_create.user_addr := g_ip4_addr;
197 f_gtp2_create_tunnel(uecups_create);
198 }
199 if (isbound(g_ip6_addr)) {
200 uecups_create.user_addr_type := IPV6;
201 uecups_create.user_addr := g_ip6_addr;
202 f_gtp2_create_tunnel(uecups_create);
203 }
204}
205
206/* create a session on the PGW */
207private function f_create_session() runs on PGW_Session_CT {
208 var PDU_GTPCv2 rx;
209
210 /* allocate + register TEID-C on local side */
211 g_teic := f_gtp2_allocate_teid();
212 g_teid := g_teic;
213
214 var template (value) FullyQualifiedTEID fteid_c_ie, fteid_u_ie;
215 fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPC, g_teic, 0,
216 f_inet_addr(mp_local_hostname_c), omit);
217 fteid_u_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPU, g_teid, 2,
218 f_inet_addr(mp_local_hostname_u), omit);
219 var template (value) PDU_GTPCv2 g2c :=
220 ts_GTP2C_CreateSessionReq(imsi := g_pars.imsi, msisdn := omit, rat_type := 6,
221 sender_fteid := fteid_c_ie,
222 apn := f_enc_dns_hostname(g_pars.apn),
223 pdn_type := g_pars.pdn_type, teid_list := { fteid_u_ie },
224 chg_car := '0000'O, bearer_id := 1);
225 /* open5gs up to 1.2.3 won't accept it without ULI, despite not mandatory */
226 var template (value) TAI tai := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0001'O };
227 var template (value) ECGI ecgi := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0'H, 23 };
228 g2c.gtpcv2_pdu.createSessionRequest.userLocationInfo := ts_GTP2C_UserLocInfo(tai := tai, ecgi := ecgi);
229
230 GTP2.send(g2c);
231 alt {
232 [] GTP2.receive(tr_GTP2C_CreateSessionResp(d_teid:=g_teic, cause:='10'O)) -> value rx {
233 /* extract TEIDs */
234 var CreateSessionResponse resp := rx.gtpcv2_pdu.createSessionResponse;
235 g_teic_remote := resp.fullyQualifiedTEID[0].tEID_GRE_Key;
236
237 /* extract allocated address[es] */
238 var PDN_Address_and_Prefix paa := resp.pDN_AddressAllocation.pDN_Address_and_Prefix;
239 if (ischosen(paa.iPv4_Address)) {
240 g_ip4_addr := paa.iPv4_Address;
241 } else if (ischosen(paa.iPv6_Address)) {
242 g_ip6_addr := paa.iPv6_Address.iPv6_Address;
243 g_ip6_plen := paa.iPv6_Address.prefixLength;
244 } else if (ischosen(paa.iPv4_IPv6)) {
245 g_ip4_addr := paa.iPv4_IPv6.iPv4_Address;
246 g_ip6_addr := paa.iPv4_IPv6.iPv6_Address;
247 g_ip6_plen := paa.iPv4_IPv6.prefixLength;
248 }
249 var integer i;
250 for (i := 0; i < lengthof(resp.bearerContextGrouped); i := i+1) {
251 var BearerContextGrouped bctx := resp.bearerContextGrouped[i];
252 select (bctx.instance) {
253 case ('0000'B) { // created
254 process_bctx_create(bctx);
255 }
256 case ('0001'B) { // removed
257 setverdict(fail, "We don't expect removed bearer contexts yet");
258 }
259 }
260 }
261 }
262 [] GTP2.receive(tr_GTP2C_CreateSessionResp(d_teid:=g_teic, cause:=?)) -> value rx {
263 setverdict(fail, "Unexpected CreateSessionResp(cause=",
264 rx.gtpcv2_pdu.createSessionResponse.cause.causeValue, ")");
265 }
266 [] GTP2.receive {
267 setverdict(fail, "Unexpected GTPv2 while waiting for CreateSessionResp");
268 }
269 }
270
271}
272
273/* delete the session from the PGW */
274private function f_delete_session(template (omit) OCT1 tx_cause := omit,
275 template (present) OCT4 exp_teid,
276 template (present) OCT1 exp_cause) runs on PGW_Session_CT {
277 var template (value) FullyQualifiedTEID fteid_c_ie
278 fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPC, g_teic, 0,
279 f_inet_addr(mp_local_hostname_c), omit);
280 var template PDU_GTPCv2 g2c :=
281 ts_GTP2C_DeleteSessionReq(d_teid := g_teic_remote, cause := tx_cause,
282 sender_fteid := fteid_c_ie,
283 teid_list := {}, bearer_id := 1);
284
285 GTP2.send(g2c);
286 alt {
287 [] GTP2.receive(tr_GTP2C_DeleteSessionResp(d_teid := exp_teid, cause := exp_cause)) {
288 setverdict(pass);
289 }
290 [] GTP2.receive(tr_GTP2C_DeleteSessionResp(?, ?)) {
291 setverdict(fail, "Unexpected DeleteSessionResp");
292 }
293 [] GTP2.receive {
294 setverdict(fail, "Unexpected GTPv2 while waiting for DeleteSessionResp");
295 }
296 }
297
298 /* destroy tunnel in daemon */
299 if (isbound(g_teid)) {
300 var UECUPS_DestroyTun uecups_destroy := {
301 local_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(mp_local_hostname_u))),
302 rx_teid := oct2int(g_teid)
303 };
304 /* FIXME: what about IPv4/IPv6 differentiation? */
305 f_gtp2_destroy_tunnel(uecups_destroy);
306 }
307}
308
309/* start a program on the user plane side; return its PID */
310private function f_start_prog(charstring command) runs on PGW_Session_CT return integer
311{
312 var UECUPS_StartProgram sprog := {
313 command := command,
314 environment := {},
315 run_as_user := mp_run_prog_as_user,
316 tun_netns_name := g_pars.tun_netns_name
317 };
318 var UECUPS_StartProgramRes res := f_gtp2_start_program(sprog);
319 if (res.result != OK) {
320 setverdict(fail, "Unable to start program '", command, "'");
321 }
322 return res.pid;
323}
324
325/* wait for termination of a given PID with specified exit_code */
326private function f_wait_term(integer pid, template (present) integer exit_code := 0,
327 float tout := 10.0) runs on PGW_Session_CT
328{
329 timer T := tout;
330
331 T.start;
332 alt {
333 [] GTP2.receive(UECUPS_ProgramTermInd:{pid := pid, exit_code := exit_code}) {
334 setverdict(pass);
335 }
336 [] GTP2.receive(UECUPS_ProgramTermInd:?) {
337 setverdict(fail, "Received unexpected ProgramTermInd");
338 }
339 [] T.timeout {
340 setverdict(fail, "timeout waiting for user-plane program termination");
341 }
342 }
343}
344
345/* execute a program and wait for result */
346private function f_start_prog_wait(charstring command, template integer exit_code := 0, float tout := 10.0) runs on PGW_Session_CT
347{
348 var integer pid := f_start_prog(command);
349 f_wait_term(pid, exit_code, tout);
350}
351
352/* execute ping command and wait for result */
353private function f_ping4(charstring host, integer interval := 1, integer count := 10) runs on PGW_Session_CT
354{
355 var charstring ping :="ping -c " & int2str(count) & " -i " & int2str(interval);
356 ping := ping & " -I " & f_inet_ntoa(g_ip4_addr);
357 ping := ping & " " & host;
Harald Welte8cfdc7c2020-04-21 22:48:34 +0200358 f_start_prog_wait(ping, tout := int2float(5 + interval*count));
Harald Welte4526da92020-03-05 23:08:10 +0100359}
360
361
362
363
364/* send echo request; expect response */
365testcase TC_tx_echo() runs on PGW_Test_CT {
366 timer T := 5.0;
367
368 f_init();
369
370 TEID0.send(ts_GTP2C_EchoReq(0));
371 T.start;
372 alt {
373 [] TEID0.receive(tr_GTP2C_EchoResp) {
374 setverdict(pass);
375 }
376 [] T.timeout {
377 setverdict(fail, "timeout waiting for Echo Response");
378 }
379 }
380}
381
382/* create a session, expect it to succeed */
383private function f_TC_createSession() runs on PGW_Session_CT {
384 f_create_session();
385 setverdict(pass);
386}
387testcase TC_createSession() runs on PGW_Test_CT {
388 var PGW_Session_CT vc_conn;
389 var SessionPars pars := valueof(t_SessionPars('001010123456789'H, "tun22"));
390 f_init();
391 vc_conn := f_start_handler(refers(f_TC_createSession), pars);
392 vc_conn.done;
393}
394
395/* create a session, then execute a ping command on the user plane */
396private function f_TC_createSession_ping4() runs on PGW_Session_CT {
397 f_create_session();
398 f_ping4(mp_ping_hostname);
399 setverdict(pass);
400}
401testcase TC_createSession_ping4() runs on PGW_Test_CT {
402 var PGW_Session_CT vc_conn;
403 var SessionPars pars := valueof(t_SessionPars('001010123456789'H, "tun23"));
404 f_init();
405 vc_conn := f_start_handler(refers(f_TC_createSession_ping4), pars);
406 vc_conn.done;
407}
Harald Weltefe595e42020-04-21 22:56:47 +0200408testcase TC_createSession_ping4_256() runs on PGW_Test_CT {
409 var PGW_Session_CT vc_conn[256];
410 var integer i;
411
412 f_init();
413
414 for (i := 0; i < sizeof(vc_conn); i:=i+1) {
415 var charstring tundev := "ping" & int2str(i);
416 var SessionPars pars := valueof(t_SessionPars(f_gen_imsi(i), tundev));
417 vc_conn[i] := f_start_handler(refers(f_TC_createSession_ping4), pars);
418 }
419
420 for (i := 0; i < lengthof(vc_conn); i:=i+1) {
421 vc_conn[i].done;
422 }
423}
424
Harald Welte4526da92020-03-05 23:08:10 +0100425
426/* create a session, then delete it again */
427private function f_TC_createSession_deleteSession() runs on PGW_Session_CT {
428 f_create_session();
429 f_delete_session(omit, g_teic, '10'O);
430 setverdict(pass);
431}
432testcase TC_createSession_deleteSession() runs on PGW_Test_CT {
433 var PGW_Session_CT vc_conn;
434 var SessionPars pars := valueof(t_SessionPars('001010123456789'H, "tun23"));
435 f_init();
436 vc_conn := f_start_handler(refers(f_TC_createSession_deleteSession), pars);
437 vc_conn.done;
438}
439
440/* send a DeleteSessionReq for an unknown/invalid TEID */
441private function f_TC_deleteSession_unknown() runs on PGW_Session_CT {
442 g_teic := f_gtp2_allocate_teid();
443 g_teic_remote := f_rnd_octstring(4);
444 f_delete_session(omit, '00000000'O, '40'O /* Context Unknown */);
445 setverdict(pass);
446}
447testcase TC_deleteSession_unknown() runs on PGW_Test_CT {
448 var PGW_Session_CT vc_conn;
449 var SessionPars pars := valueof(t_SessionPars('001010123456789'H, "tun23"));
450 f_init();
451 vc_conn := f_start_handler(refers(f_TC_deleteSession_unknown), pars);
452 vc_conn.done;
453}
454
455
456
457
458control {
459 execute( TC_tx_echo() );
460 execute( TC_createSession() );
461 execute( TC_createSession_ping4() );
Harald Weltefe595e42020-04-21 22:56:47 +0200462 execute( TC_createSession_ping4_256() );
Harald Welte4526da92020-03-05 23:08:10 +0100463 execute( TC_createSession_deleteSession() );
464 execute( TC_deleteSession_unknown() );
465}
466
467
468}