blob: c1e29d8a421c6f0bf16b8b75139daeed88cce312 [file] [log] [blame]
Harald Welte78cae922018-03-01 10:12:56 +01001module HLR_Tests {
Harald Weltedf327232017-12-28 22:51:51 +01002
3import from GSUP_Types all;
4import from IPA_Emulation all;
5
Harald Weltec2c52552018-03-01 21:20:39 +01006import from General_Types all;
7import from Osmocom_Types all;
Harald Welte39b82d32018-03-01 10:21:29 +01008import from Osmocom_CTRL_Adapter all;
9
10import from Osmocom_VTY_Functions all;
11import from TELNETasp_PortType all;
12
13type component test_CT extends CTRL_Adapter_CT {
Harald Weltedf327232017-12-28 22:51:51 +010014 var IPA_Emulation_CT vc_IPA;
15 var IPA_CCM_Parameters ccm_pars;
16 port IPA_GSUP_PT GSUP;
Harald Welte39b82d32018-03-01 10:21:29 +010017
18 port TELNETasp_PT VTY;
Harald Weltec2c52552018-03-01 21:20:39 +010019
20 timer g_Tguard := 10.0;
Harald Weltedf327232017-12-28 22:51:51 +010021};
22
23modulepar {
24 charstring mp_hlr_ip := "127.0.0.1";
25 integer mp_hlr_gsup_port := 4222;
26 integer mp_hlr_ctrl_port := 4259;
27};
28
Harald Weltec2c52552018-03-01 21:20:39 +010029type record HlrSubscrAud2G {
30 charstring algo,
31 OCT16 ki
32}
33
34type record HlrSubscrAud3G {
35 charstring algo,
36 OCT16 k,
37 OCT16 op,
38 boolean op_is_opc
39}
40
41type record HlrSubscriber {
42 hexstring imsi,
43 hexstring msisdn,
44 HlrSubscrAud2G aud2g optional,
45 HlrSubscrAud3G aud3g optional
46}
47
48type record of HlrSubscriber HlrSubscriberList;
49
50template (value) HlrSubscriber t_SubNoAuth(hexstring imsi, hexstring msisdn) := {
51 imsi := imsi,
52 msisdn := msisdn,
53 aud2g := omit,
54 aud3g := omit
55}
56
57const OCT16 c_KI_DEFAULT := '000102030405060708090a0b0c0d0e0f'O;
58const OCT16 c_K_DEFAULT := '101112131415161718191a1b1c1d1e1f'O;
59const OCT16 c_OP_DEFAULT := '202122232425262728292a2b2c2d2e2f'O;
60//const OCT16 c_OPC_DEFAULT := '303132333435363738393a3b3c3d3f'O;
61
62template (value) HlrSubscriber t_Sub2G(hexstring imsi, hexstring msisdn, charstring algo) := {
63 imsi := imsi,
64 msisdn := msisdn,
65 aud2g := {
66 algo := algo,
67 ki := c_KI_DEFAULT
68 },
69 aud3g := omit
70}
71
72template (value) HlrSubscriber t_Sub3G(hexstring imsi, hexstring msisdn, charstring algo, boolean is_opc) := {
73 imsi := imsi,
74 msisdn := msisdn,
75 aud2g := omit,
76 aud3g := {
77 algo := algo,
78 k := c_K_DEFAULT,
79 op := c_OP_DEFAULT,
80 op_is_opc := is_opc
81 }
82}
83
84template (value) HlrSubscriber t_Sub2G3G(hexstring imsi, hexstring msisdn, charstring algo2g, charstring algo3g, boolean is_opc) := {
85 imsi := imsi,
86 msisdn := msisdn,
87 aud2g := {
88 algo := algo2g,
89 ki := c_KI_DEFAULT
90 },
91 aud3g := {
92 algo := algo3g,
93 k := c_K_DEFAULT,
94 op := c_OP_DEFAULT,
95 op_is_opc := is_opc
96 }
97}
98
99/* generate a variety of subscribers with different parameters */
100function f_gen_subs() runs on test_CT return HlrSubscriberList {
101 var HlrSubscriber sub;
102 var HlrSubscriberList sl := {};
103
104 sub := valueof(t_Sub2G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9), "comp128v1"));
105 sl := sl & { sub };
106
107 sub := valueof(t_Sub2G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9), "comp128v2"));
108 sl := sl & { sub };
109
110 sub := valueof(t_Sub2G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9), "comp128v3"));
111 sl := sl & { sub };
112
113 sub := valueof(t_Sub3G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9), "milenage", false));
114 sl := sl & { sub };
115
116 sub := valueof(t_Sub3G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9), "milenage", true));
117 sl := sl & { sub };
118
119 sub := valueof(t_Sub2G3G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9),
120 "comp128v1", "milenage", false));
121 sl := sl & { sub };
122
123 sub := valueof(t_Sub2G3G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9),
124 "comp128v2", "milenage", false));
125 sl := sl & { sub };
126
127 sub := valueof(t_Sub2G3G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9),
128 "comp128v3", "milenage", false));
129 sl := sl & { sub };
130
131 return sl;
132}
133
Harald Welte39b82d32018-03-01 10:21:29 +0100134function f_init_vty() runs on test_CT {
135 map(self:VTY, system:VTY);
136 f_vty_set_prompts(VTY);
137 f_vty_transceive(VTY, "enable");
138}
139
Harald Weltec2c52552018-03-01 21:20:39 +0100140private altstep as_Tguard() runs on test_CT {
141 [] g_Tguard.timeout {
142 setverdict(fail, "g_Tguard timeout");
143 self.stop;
144 }
145}
146
Harald Weltedf327232017-12-28 22:51:51 +0100147function f_init() runs on test_CT {
Harald Weltec2c52552018-03-01 21:20:39 +0100148
149 /* activate default guard timer to ensure all tests eventually terminate */
150 g_Tguard.start;
151 activate(as_Tguard());
152
Harald Weltedf327232017-12-28 22:51:51 +0100153 ccm_pars := c_IPA_default_ccm_pars;
154 ccm_pars.name := "Osmocom TTCN-3 GSUP Simulator";
155
156 vc_IPA := IPA_Emulation_CT.create("IPA");
157 map(vc_IPA:IPA_PORT, system:IPA_CODEC_PT);
158 connect(vc_IPA:IPA_GSUP_PORT, self:GSUP);
159 vc_IPA.start(IPA_Emulation.main_client(mp_hlr_ip, mp_hlr_gsup_port, "", -1, ccm_pars));
Harald Welte39b82d32018-03-01 10:21:29 +0100160
Harald Welte79420cc2018-03-01 16:49:29 +0100161 timer T:= 10.0;
162 alt {
163 [] GSUP.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_UP}) { }
164 [] T.timeout {
165 setverdict(fail, "Timeout waiting for GSUP IPA Link to come up");
166 self.stop;
167 }
168 }
169
Harald Welte39b82d32018-03-01 10:21:29 +0100170 f_init_vty();
171
172 f_ipa_ctrl_start(mp_hlr_ip, mp_hlr_ctrl_port);
Harald Weltedf327232017-12-28 22:51:51 +0100173}
174
Harald Weltec2c52552018-03-01 21:20:39 +0100175function f_vty_transceive_match(TELNETasp_PT pt, charstring cmd, template charstring exp_ret) {
176 var charstring ret := f_vty_transceive_ret(pt, cmd);
177 if (not match(ret, exp_ret)) {
178 setverdict(fail, "Non-matching VTY response: ", ret);
179 self.stop;
Harald Weltedf327232017-12-28 22:51:51 +0100180 }
181}
182
Harald Welte70296062018-03-01 22:42:03 +0100183private template (value) charstring t_subscr_prefix(hexstring imsi) :=
184 "subscriber imsi " & hex2str(imsi) & " ";
185
Harald Weltec2c52552018-03-01 21:20:39 +0100186/* create a given subscriber using the VTY */
187function f_vty_subscr_create(HlrSubscriber sub) runs on test_CT {
Harald Welte70296062018-03-01 22:42:03 +0100188 var charstring prefix := valueof(t_subscr_prefix(sub.imsi));
Harald Weltec2c52552018-03-01 21:20:39 +0100189 f_vty_transceive_match(VTY, prefix & "create", pattern "% Created subscriber *");
190 f_vty_transceive_match(VTY, prefix & "update msisdn " & hex2str(sub.msisdn),
191 pattern "% Updated subscriber *");
192 if (ispresent(sub.aud2g)) {
193 f_vty_transceive_match(VTY, prefix & "update aud2g " & sub.aud2g.algo &
194 " ki " & oct2str(sub.aud2g.ki),
195 pattern "");
196 } else {
197 f_vty_transceive_match(VTY, prefix & "update aud2g none", pattern "");
198 }
199
200 if (ispresent(sub.aud3g)) {
201 var charstring op_mode := "op";
202 if (sub.aud3g.op_is_opc) {
203 op_mode := "opc";
204 }
205 f_vty_transceive_match(VTY, prefix & "update aud3g " & sub.aud3g.algo &
206 " k " & oct2str(sub.aud3g.k) & " " & op_mode & " " &
207 oct2str(sub.aud3g.op), pattern "");
208 } else {
209 f_vty_transceive_match(VTY, prefix & "update aud3g none", pattern "");
210 }
211}
212
Harald Welte09b3c502018-03-01 22:42:22 +0100213function f_vty_subscr_update_msisdn(HlrSubscriber sub, hexstring new_msisdn) runs on test_CT {
214 var charstring prefix := valueof(t_subscr_prefix(sub.imsi));
215 f_vty_transceive_match(VTY, prefix & "update msisdn " & hex2str(new_msisdn),
216 pattern "% Updated subscriber *");
217}
218
Harald Weltec2c52552018-03-01 21:20:39 +0100219/* perform 'delete' on subscriber */
220function f_vty_subscr_delete(HlrSubscriber sub) runs on test_CT {
Harald Welte70296062018-03-01 22:42:03 +0100221 var charstring prefix := valueof(t_subscr_prefix(sub.imsi));
Harald Weltec2c52552018-03-01 21:20:39 +0100222 f_vty_transceive_match(VTY, prefix & "delete",
223 pattern "% Deleted subscriber for IMSI *");
224}
225
226/* perform 'show' on subscriber; match result with pattern 'exp' */
227function f_vty_subscr_show(HlrSubscriber sub, template charstring exp) runs on test_CT {
Harald Welte70296062018-03-01 22:42:03 +0100228 var charstring prefix := valueof(t_subscr_prefix(sub.imsi));
Harald Weltec2c52552018-03-01 21:20:39 +0100229 f_vty_transceive_match(VTY, prefix & "show", exp);
230}
231
232
233/* perform SendAuthInfo for given imsi, return the GSUP response/error */
234function f_perform_SAI(hexstring imsi, template (omit) integer exp_err_cause := omit)
235runs on test_CT return GSUP_PDU {
236 var GSUP_PDU ret;
237 timer T := 3.0;
238 var boolean exp_fail := false;
239 if (not istemplatekind(exp_err_cause, "omit")) {
240 exp_fail := true;
241 }
242
243 GSUP.send(valueof(ts_GSUP_SAI_REQ(imsi)));
244 T.start;
245 alt {
246 [exp_fail] GSUP.receive(tr_GSUP_SAI_ERR(imsi, exp_err_cause)) -> value ret {
247 setverdict(pass);
248 }
249 [exp_fail] GSUP.receive(tr_GSUP_SAI_ERR(imsi, ?)) -> value ret {
250 setverdict(fail, "Unexpected SAI ERROR Cause");
251 }
252 [exp_fail] GSUP.receive(tr_GSUP_SAI_RES(imsi)) -> value ret {
253 setverdict(fail, "Unexpected SAI.res for unknown IMSI");
254 }
255 [not exp_fail] GSUP.receive(tr_GSUP_SAI_ERR(imsi, ?)) -> value ret {
256 setverdict(fail, "Unexpected SAI ERROR");
257 }
258 [not exp_fail] GSUP.receive(tr_GSUP_SAI_RES(imsi)) -> value ret {
259 setverdict(pass);
260 }
261 [] GSUP.receive { repeat; }
262 [] T.timeout {
263 setverdict(fail, "Timeout waiting for SAI response");
264 self.stop;
265 }
266 }
267 return ret;
268}
269
270function f_perform_UL(hexstring imsi, template hexstring msisdn,
271 template (omit) integer exp_err_cause := omit)
272runs on test_CT return GSUP_PDU {
273 var GSUP_PDU ret;
274 timer T := 3.0;
275 var boolean exp_fail := false;
276 var boolean isd_done := false;
277 if (not istemplatekind(exp_err_cause, "omit")) {
278 exp_fail := true;
279 }
280
281 GSUP.send(valueof(ts_GSUP_UL_REQ(imsi)));
282 T.start;
283 alt {
284 [exp_fail] GSUP.receive(tr_GSUP_UL_ERR(imsi, exp_err_cause)) -> value ret {
285 setverdict(pass);
286 }
287 [exp_fail] GSUP.receive(tr_GSUP_UL_ERR(imsi, ?)) -> value ret {
288 setverdict(fail, "Unexpected UL ERROR Cause");
289 }
290 [exp_fail] GSUP.receive(tr_GSUP_UL_RES(imsi)) -> value ret {
291 setverdict(fail, "Unexpected UL.res for unknown IMSI");
292 }
293 [exp_fail] GSUP.receive(tr_GSUP_ISD_REQ(imsi)) -> value ret {
294 setverdict(fail, "Unexpected ISD.req in error case");
295 }
296 [not exp_fail] GSUP.receive(tr_GSUP_UL_ERR(imsi, ?)) -> value ret {
297 setverdict(fail, "Unexpected UL ERROR");
298 }
299 [not exp_fail and not isd_done] GSUP.receive(tr_GSUP_ISD_REQ(imsi, msisdn)) -> value ret {
300 GSUP.send(ts_GSUP_ISD_RES(imsi));
301 isd_done := true;
Harald Welte8f0c9332018-03-01 23:38:40 +0100302 repeat;
Harald Weltec2c52552018-03-01 21:20:39 +0100303 }
304 [not exp_fail and isd_done] GSUP.receive(tr_GSUP_UL_RES(imsi)) -> value ret {
305 setverdict(pass);
306 }
307 [] GSUP.receive { repeat; }
308 [] T.timeout {
309 setverdict(fail, "Timeout waiting for UL response");
310 self.stop;
311 }
312 }
313 return ret;
314}
315
Harald Welte958f8b42018-03-01 23:40:17 +0100316/* perform PurgeMS for given imsi, return the GSUP response/error */
317function f_perform_PURGE(hexstring imsi, GSUP_CnDomain cn_dom,
318 template (omit) integer exp_err_cause := omit)
319runs on test_CT return GSUP_PDU {
320 var GSUP_PDU ret;
321 timer T := 3.0;
322 var boolean exp_fail := false;
323 if (not istemplatekind(exp_err_cause, "omit")) {
324 exp_fail := true;
325 }
326
327 GSUP.send(valueof(ts_GSUP_PURGE_MS_REQ(imsi, cn_dom)));
328 T.start;
329 alt {
330 [exp_fail] GSUP.receive(tr_GSUP_PURGE_MS_ERR(imsi, exp_err_cause)) -> value ret {
331 setverdict(pass);
332 }
333 [exp_fail] GSUP.receive(tr_GSUP_PURGE_MS_ERR(imsi, ?)) -> value ret {
334 setverdict(fail, "Unexpected PURGE ERROR Cause");
335 }
336 [exp_fail] GSUP.receive(tr_GSUP_PURGE_MS_RES(imsi)) -> value ret {
337 setverdict(fail, "Unexpected PURGE.res for unknown IMSI");
338 }
339 [not exp_fail] GSUP.receive(tr_GSUP_PURGE_MS_ERR(imsi, ?)) -> value ret {
340 setverdict(fail, "Unexpected PURGE ERROR");
341 }
342 [not exp_fail] GSUP.receive(tr_GSUP_PURGE_MS_RES(imsi)) -> value ret {
343 setverdict(pass);
344 }
345 [] GSUP.receive { repeat; }
346 [] T.timeout {
347 setverdict(fail, "Timeout waiting for PURGE response");
348 self.stop;
349 }
350 }
351 return ret;
352}
353
354
Harald Weltec2c52552018-03-01 21:20:39 +0100355testcase TC_gsup_sai_err_invalid_imsi() runs on test_CT {
356 var hexstring imsi := '01234'H;
357 var GSUP_PDU res;
358
359 f_init();
360
361 res := f_perform_SAI(imsi, 17);
362 setverdict(pass);
363}
364
365/* test SAI for a number of different subscriber cases (algo, 2g/3g, ...) */
366testcase TC_gsup_sai() runs on test_CT {
367 var HlrSubscriberList sl;
368 var GSUP_PDU res;
369
370 f_init();
371
372 sl := f_gen_subs();
373 for (var integer i := 0; i < sizeof(sl); i := i+1) {
374 var HlrSubscriber sub := sl[i];
375 log("Testing SAI for ", sub);
376 f_vty_subscr_create(sub);
377 res := f_perform_SAI(sub.imsi);
378 /* TODO: match if tuple[s] matches expectation */
379 f_vty_subscr_delete(sub);
380 }
381
382 setverdict(pass);
383}
384
385/* test UL for unknown IMSI */
386testcase TC_gsup_ul_unknown_imsi() runs on test_CT {
387 var hexstring imsi := f_rnd_imsi('26242'H);
388 var GSUP_PDU res;
389
390 f_init();
391
392 res := f_perform_UL(imsi, ?, 2);
393 setverdict(pass);
394}
395
396/* test SAI for a number of different subscriber cases (algo, 2g/3g, ...) */
397testcase TC_gsup_ul() runs on test_CT {
398 var HlrSubscriberList sl;
399 var GSUP_PDU res;
400
401 f_init();
402
403 sl := f_gen_subs();
404 for (var integer i := 0; i < sizeof(sl); i := i+1) {
405 var HlrSubscriber sub := sl[i];
406 log("Testing UL for ", sub);
407 f_vty_subscr_create(sub);
408 res := f_perform_UL(sub.imsi, sub.msisdn);
409 f_vty_subscr_delete(sub);
410 }
411
412 setverdict(pass);
413}
414
415/* Test only the VTY commands */
416testcase TC_vty() runs on test_CT {
417 var HlrSubscriber sub;
418
419 f_init();
420
421 /* we're not using f_gen_subs() here as the expect pattern for the 'show' are different
422 * from case to case */
423 sub := valueof(t_Sub2G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9), "comp128v1"));
424 f_vty_subscr_create(sub);
425 f_vty_subscr_show(sub, pattern "*IMSI: *2G auth: COMP128v1*");
426 f_vty_subscr_delete(sub);
427
428 sub := valueof(t_Sub3G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9),
429 "milenage", false));
430 f_vty_subscr_create(sub);
431 f_vty_subscr_show(sub, pattern "*IMSI: *3G auth: MILENAGE*");
432 f_vty_subscr_delete(sub);
433
434 sub := valueof(t_Sub2G3G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9),
435 "comp128v1", "milenage", false));
436 f_vty_subscr_create(sub);
437 f_vty_subscr_show(sub, pattern "*IMSI: *2G auth: COMP128v1*3G auth: MILENAGE*");
438 f_vty_subscr_delete(sub);
439
440 setverdict(pass);
441}
442
Harald Welte09b3c502018-03-01 22:42:22 +0100443/* VTY changes to MSISDN should result in ISD to current VLR */
444testcase TC_vty_msisdn_isd() runs on test_CT {
445 var HlrSubscriber sub;
446 var hexstring new_msisdn;
447 var GSUP_PDU res;
448 timer T := 5.0;
449
450 f_init();
451
452 /* Create Subscriber */
453 sub := valueof(t_Sub2G(f_rnd_imsi('26242'H), '49161'H & f_rnd_hexstring(7, 9), "comp128v1"));
454 f_vty_subscr_create(sub);
455
456 /* Perform UpdateLocation (VLR now known to HLR) */
457 res := f_perform_UL(sub.imsi, sub.msisdn);
458
459 /* Then change IMSI via VTY */
460 new_msisdn := '49161'H & f_rnd_hexstring(7, 9);
461 f_vty_subscr_update_msisdn(sub, new_msisdn);
462 /* And expect InsertSubscriberData as result */
463 T.start;
464 alt {
465 [] GSUP.receive(tr_GSUP_ISD_REQ(sub.imsi, new_msisdn)) {
466 GSUP.send(ts_GSUP_ISD_RES(sub.imsi));
467 sub.msisdn := new_msisdn;
468 setverdict(pass);
469 }
470 [] GSUP.receive { repeat; }
471 [] T.timeout {
472 setverdict(fail, "Timeout waiting for ISD.req");
473 }
474 }
475}
476
Harald Welte958f8b42018-03-01 23:40:17 +0100477/* Test PURGE MS for CS services */
478testcase TC_gsup_purge_cs() runs on test_CT {
479 var HlrSubscriberList sl;
480 var GSUP_PDU res;
481
482 f_init();
483
484 sl := f_gen_subs();
485 for (var integer i := 0; i < sizeof(sl); i := i+1) {
486 var HlrSubscriber sub := sl[i];
487 log("Testing CS Purge for ", sub);
488 f_vty_subscr_create(sub);
489 res := f_perform_UL(sub.imsi, sub.msisdn);
490 res := f_perform_PURGE(sub.imsi, OSMO_GSUP_CN_DOMAIN_CS);
491 f_vty_subscr_delete(sub);
492 }
493
494 setverdict(pass);
495}
496
497/* Test PURGE MS for PS services */
498testcase TC_gsup_purge_ps() runs on test_CT {
499 var HlrSubscriberList sl;
500 var GSUP_PDU res;
501
502 f_init();
503
504 sl := f_gen_subs();
505 for (var integer i := 0; i < sizeof(sl); i := i+1) {
506 var HlrSubscriber sub := sl[i];
507 log("Testing PS Purge for ", sub);
508 f_vty_subscr_create(sub);
509 res := f_perform_UL(sub.imsi, sub.msisdn);
510 res := f_perform_PURGE(sub.imsi, OSMO_GSUP_CN_DOMAIN_PS);
511 f_vty_subscr_delete(sub);
512 }
513
514 setverdict(pass);
515}
516
517/* Test PURGEG MS procedure for unknown IMSI */
518testcase TC_gsup_purge_unknown() runs on test_CT {
519 var hexstring imsi := '2345743413463'H;
520 var GSUP_PDU res;
521
522 f_init();
523
524 res := f_perform_PURGE(imsi, OSMO_GSUP_CN_DOMAIN_CS, 2);
525
526 setverdict(pass);
527}
528
529
Harald Weltec2c52552018-03-01 21:20:39 +0100530/* TODO:
531 * UL with ISD error
532 * UL with ISD timeout
533 * PURGE MS
534 * LOCATION CANCEL
535 * AUTH FAIL REP
536 * DELETE DATA after hlr_subscr_nam() change
537 * correctness
538 ** wrong message type
539 ** wrong length of PDU
540 ** too short message
541 ** missing IMSI IE
542
543 */
544
Harald Weltedf327232017-12-28 22:51:51 +0100545control {
546 execute( TC_gsup_sai_err_invalid_imsi() );
Harald Weltec2c52552018-03-01 21:20:39 +0100547 execute( TC_gsup_sai() );
548 execute( TC_gsup_ul_unknown_imsi() );
549 execute( TC_gsup_ul() );
550 execute( TC_vty() );
Harald Welte09b3c502018-03-01 22:42:22 +0100551 execute( TC_vty_msisdn_isd() );
Harald Welte958f8b42018-03-01 23:40:17 +0100552 execute( TC_gsup_purge_cs() );
553 execute( TC_gsup_purge_ps() );
554 execute( TC_gsup_purge_unknown() );
Harald Weltedf327232017-12-28 22:51:51 +0100555};
556
557};