pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/28172 )
Change subject: ggsn: Validate charging reported values ......................................................................
ggsn: Validate charging reported values
Change-Id: I497309bb0b30c61bdb00e0c08f18294ecd4dd485 --- M ggsn_tests/GGSN_Tests.ttcn M library/DIAMETER_Emulation.ttcn M library/DIAMETER_Templates.ttcn 3 files changed, 122 insertions(+), 30 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/72/28172/1
diff --git a/ggsn_tests/GGSN_Tests.ttcn b/ggsn_tests/GGSN_Tests.ttcn index 4a5f0ba..dffe0bf 100644 --- a/ggsn_tests/GGSN_Tests.ttcn +++ b/ggsn_tests/GGSN_Tests.ttcn @@ -652,8 +652,8 @@ return tpl; }
- private altstep as_DIA_Gy_CCR(template (omit) PdpContext ctx, DCC_NONE_CC_Request_Type req_type) runs on GT_CT { - var PDU_DIAMETER rx_dia; + private altstep as_DIA_Gy_CCR(template (omit) PdpContext ctx, DCC_NONE_CC_Request_Type req_type, + out PDU_DIAMETER rx_dia) runs on GT_CT { [] Gy_UNIT.receive(f_tr_DIA_Gy_CCR(ctx, req_type := req_type)) -> value rx_dia { var template (value) PDU_DIAMETER tx_dia; var template (omit) AVP avp; @@ -685,6 +685,7 @@ var Gtp1cUnitdata ud; var CreatePDPContextResponse cpr; var default d; + var PDU_DIAMETER rx_dia;
log("sending CreatePDP"); f_send_gtpc(ts_GTPC_CreatePDP(g_peer_c, g_c_seq_nr, ctx.imsi, g_restart_ctr, @@ -697,7 +698,7 @@ as_DIA_Gx_CCR(INITIAL_REQUEST); } if (Gy_PROC.checkstate("Connected")) { - as_DIA_Gy_CCR(ctx, INITIAL_REQUEST); + as_DIA_Gy_CCR(ctx, INITIAL_REQUEST, rx_dia); } alt { [] GTPC.receive(tr_GTPC_MsgType(g_peer_c, createPDPContextResponse, ctx.teic)) -> value ud { @@ -742,7 +743,7 @@ T_default.stop; }
- function f_pdp_ctx_del(PdpContext ctx, template BIT1 teardown_ind, OCT1 expect_causevalue := '80'O, boolean expect_diameter := true) runs on GT_CT { + function f_pdp_ctx_del_out(PdpContext ctx, out PDU_DIAMETER rx_dia, template BIT1 teardown_ind, OCT1 expect_causevalue := '80'O, boolean expect_diameter := true) runs on GT_CT { var Gtp1cUnitdata ud; var default d; var OCT4 expect_teid; @@ -762,7 +763,7 @@ as_DIA_Gx_CCR(TERMINATION_REQUEST); } if (Gy_PROC.checkstate("Connected") and expect_diameter) { - as_DIA_Gy_CCR(ctx, TERMINATION_REQUEST); + as_DIA_Gy_CCR(ctx, TERMINATION_REQUEST, rx_dia); } alt { [] GTPC.receive(tr_GTPC_MsgType(g_peer_c, deletePDPContextResponse, expect_teid)) -> value ud { @@ -777,6 +778,10 @@ deactivate(d); T_default.stop; } + function f_pdp_ctx_del(PdpContext ctx, template BIT1 teardown_ind, OCT1 expect_causevalue := '80'O, boolean expect_diameter := true) runs on GT_CT { + var PDU_DIAMETER rx_dia; + f_pdp_ctx_del_out(ctx, rx_dia, teardown_ind, expect_causevalue, expect_diameter) + }
/* send a Update PdP Context Request, expect Response */ function f_pdp_ctx_update(inout PdpContext ctx, OCT1 exp_cause := '80'O, template (omit) OCT4 new_teid := omit, template (omit) OCT4 new_teic := omit) runs on GT_CT { @@ -2168,6 +2173,7 @@ var OCT4 saddr; var integer teic; var integer idx; + var PDU_DIAMETER rx_dia;
f_init();
@@ -2183,7 +2189,7 @@ T_next.start; alt { [Gx_PROC.checkstate("Connected")] as_DIA_Gx_CCR(INITIAL_REQUEST) { repeat; } - [Gy_PROC.checkstate("Connected")] as_DIA_Gy_CCR(omit, INITIAL_REQUEST) { repeat; } + [Gy_PROC.checkstate("Connected")] as_DIA_Gy_CCR(omit, INITIAL_REQUEST, rx_dia) { repeat; } [] pingpong(); [] T_next.timeout { f_send_gtpc(ts_GTPC_CreatePDP(g_peer_c, g_c_seq_nr, ctx[next_req_ctx].imsi, g_restart_ctr, @@ -2239,7 +2245,7 @@ T_next.start; alt { [Gx_PROC.checkstate("Connected")] as_DIA_Gx_CCR(TERMINATION_REQUEST) { repeat; } - [Gy_PROC.checkstate("Connected")] as_DIA_Gy_CCR(omit, TERMINATION_REQUEST) { repeat; } + [Gy_PROC.checkstate("Connected")] as_DIA_Gy_CCR(omit, TERMINATION_REQUEST, rx_dia) { repeat; } [] pingpong(); [] T_next.timeout { f_send_gtpc(ts_GTPC_DeletePDP(g_peer_c, g_c_seq_nr, ctx[next_req_ctx].teic_remote, ctx[next_req_ctx].nsapi, '1'B)); @@ -2284,6 +2290,7 @@ var CreatePDPContextResponse cpr; var TEIClist teic_list := {}; var integer teic; + var PDU_DIAMETER rx_dia;
f_init();
@@ -2292,7 +2299,7 @@ T_next.start; alt { [Gx_PROC.checkstate("Connected")] as_DIA_Gx_CCR(INITIAL_REQUEST) { repeat; } - [Gy_PROC.checkstate("Connected")] as_DIA_Gy_CCR(omit, INITIAL_REQUEST) { repeat; } + [Gy_PROC.checkstate("Connected")] as_DIA_Gy_CCR(omit, INITIAL_REQUEST, rx_dia) { repeat; } [] pingpong(); [] T_next.timeout { if (cont_req) { @@ -2343,7 +2350,7 @@ T_next.start; alt { [Gx_PROC.checkstate("Connected")] as_DIA_Gx_CCR(TERMINATION_REQUEST) { repeat; } - [Gy_PROC.checkstate("Connected")] as_DIA_Gy_CCR(omit, TERMINATION_REQUEST) { repeat; } + [Gy_PROC.checkstate("Connected")] as_DIA_Gy_CCR(omit, TERMINATION_REQUEST, rx_dia) { repeat; } [] pingpong(); [] T_next.timeout { f_send_gtpc(ts_GTPC_DeletePDP(g_peer_c, g_c_seq_nr, teic_list[next_req_ctx], '0001'B, '1'B)); @@ -2374,9 +2381,50 @@ f_shutdown_helper(); }
+ private function f_validate_gy_cc_report(PDU_DIAMETER rx_dia, template (present) DCA_3GPP_Reporting_Reason repreason_exp := ?, + template (present) integer cc_time_exp := ?, + template (present) integer cc_in_oct_exp := ?, + template (present) integer cc_out_oct_exp := ?) + { + var AVP multi_services_cc, used_service_unit; + var AVP_Grouped multi_services_cc_data, used_service_unit_data; + var AVP repreason, cc_time, cc_in_oct, cc_out_oct; + + multi_services_cc := f_DIAMETER_get_avp_or_fail(rx_dia, c_AVP_Code_DCC_NONE_Multiple_Services_Credit_Control); + multi_services_cc_data := valueof(multi_services_cc.avp_data.avp_DCC_NONE_Multiple_Services_Credit_Control); + + repreason := f_AVP_Grouped_get_avp_or_fail(multi_services_cc_data, c_AVP_Code_DCA_3GPP_Reporting_Reason); + if (not match(repreason.avp_data.avp_DCA_3GPP_Reporting_Reason, repreason_exp)) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("3GPP-Reporting-Reason mismatch ", repreason, " vs exp ", repreason_exp)); + } + + used_service_unit := f_AVP_Grouped_get_avp_or_fail(multi_services_cc_data, c_AVP_Code_DCC_NONE_Used_Service_Unit); + used_service_unit_data := valueof(used_service_unit.avp_data.avp_DCC_NONE_Used_Service_Unit); + + cc_time := f_AVP_Grouped_get_avp_or_fail(used_service_unit_data, c_AVP_Code_DCC_NONE_CC_Time); + if (not match(oct2int(cc_time.avp_data.avp_DCC_NONE_CC_Time), cc_time_exp)) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("3GPP-CC-Time mismatch ", cc_time, " vs exp ", cc_time_exp)); + } + + cc_in_oct := f_AVP_Grouped_get_avp_or_fail(used_service_unit_data, c_AVP_Code_DCC_NONE_CC_Input_Octets); + if (not match(oct2int(cc_in_oct.avp_data.avp_DCC_NONE_CC_Input_Octets), cc_in_oct_exp)) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("3GPP-CC-Input-Octets mismatch ", cc_in_oct, " vs exp ", cc_in_oct_exp)); + } + + cc_out_oct := f_AVP_Grouped_get_avp_or_fail(used_service_unit_data, c_AVP_Code_DCC_NONE_CC_Output_Octets); + if (not match(oct2int(cc_out_oct.avp_data.avp_DCC_NONE_CC_Output_Octets), cc_out_oct_exp)) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("3GPP-CC-Output-Octets mismatch ", cc_out_oct, " vs exp ", cc_out_oct_exp)); + } + } + /* Test charging over Gy interface. */ testcase TC_gy_charging_cc_time() runs on GT_CT { var default d; + var PDU_DIAMETER rx_dia;
g_gy_validity_time := 3; /* Grant access for 3 seconds, needs to be re-validated afterwards */ f_init(); @@ -2396,17 +2444,20 @@
g_gy_validity_time := 2; /* First update reports octests/pkt on both UL/DL (see icmp ping-pong above) */ - as_DIA_Gy_CCR(ctx, UPDATE_REQUEST); + as_DIA_Gy_CCR(ctx, UPDATE_REQUEST, rx_dia); + f_validate_gy_cc_report(rx_dia, VALIDITY_TIME, (3..4), 28, 28);
/* Second update: 0 ul/dl pkt/octet should be reported, since nothing was sent */ - as_DIA_Gy_CCR(ctx, UPDATE_REQUEST); + as_DIA_Gy_CCR(ctx, UPDATE_REQUEST, rx_dia); + f_validate_gy_cc_report(rx_dia, VALIDITY_TIME, (2..3), 0, 0);
/* Third update: make sure report contains again octets/pkts for both UL/DL: */ f_send_gtpu(ctx, f_gen_icmpv4_echo(saddr, dns1_addr)); f_wait_icmp4_echo_reply(ctx); f_send_gtpu(ctx, f_gen_icmpv4_echo(saddr, dns1_addr)); f_wait_icmp4_echo_reply(ctx); - as_DIA_Gy_CCR(ctx, UPDATE_REQUEST); + as_DIA_Gy_CCR(ctx, UPDATE_REQUEST, rx_dia); + f_validate_gy_cc_report(rx_dia, VALIDITY_TIME, (2..3), 56, 56);
/* Let the CCA reach the GGSN */ f_sleep(0.5); @@ -2418,7 +2469,8 @@ f_send_gtpu(ctx, f_gen_icmpv4_echo(saddr, dns1_addr)); f_wait_icmp4_echo_reply(ctx);
- f_pdp_ctx_del(ctx, '1'B); + f_pdp_ctx_del_out(ctx, rx_dia, '1'B); + f_validate_gy_cc_report(rx_dia, FINAL, (0..2), 28, 28);
f_shutdown_helper(); diff --git a/library/DIAMETER_Emulation.ttcn b/library/DIAMETER_Emulation.ttcn index 748b452..09f92be 100644 --- a/library/DIAMETER_Emulation.ttcn +++ b/library/DIAMETER_Emulation.ttcn @@ -211,23 +211,6 @@ } }
-function f_DIAMETER_get_avp(PDU_DIAMETER pdu, template (present) AVP_Code avp_code) -return template (omit) AVP -{ - var integer i; - - for (i := 0; i < lengthof(pdu.avps); i := i+1) { - if (not ispresent(pdu.avps[i].avp)) { - continue; - } - var AVP_Header hdr := pdu.avps[i].avp.avp_header; - if (match(hdr.avp_code, avp_code)) { - return pdu.avps[i].avp; - } - } - return omit; -} - function f_DIAMETER_get_imsi(PDU_DIAMETER pdu) return template (omit) IMSI { var template (omit) AVP imsi_avp; diff --git a/library/DIAMETER_Templates.ttcn b/library/DIAMETER_Templates.ttcn index 855358d..38c44e8 100644 --- a/library/DIAMETER_Templates.ttcn +++ b/library/DIAMETER_Templates.ttcn @@ -11,6 +11,7 @@
import from DIAMETER_Types all; import from Osmocom_Types all; +import from Misc_Helpers all;
/* https://www.iana.org/assignments/aaa-parameters/aaa-parameters.xhtml#aaa-par... */ type enumerated DIAMETER_Resultcode { @@ -1611,4 +1612,60 @@ tr_AVP_OriginStateId(state_id) ));
+function f_DIAMETER_get_avp(PDU_DIAMETER pdu, template (present) AVP_Code avp_code) +return template (omit) AVP +{ + var integer i; + + for (i := 0; i < lengthof(pdu.avps); i := i+1) { + if (not ispresent(pdu.avps[i].avp)) { + continue; + } + var AVP_Header hdr := pdu.avps[i].avp.avp_header; + if (match(hdr.avp_code, avp_code)) { + return pdu.avps[i].avp; + } + } + return omit; +} +function f_DIAMETER_get_avp_or_fail(PDU_DIAMETER pdu, template (present) AVP_Code avp_code) +return AVP +{ + var template (omit) AVP avp; + avp := f_DIAMETER_get_avp(pdu, avp_code); + if (istemplatekind(avp, "omit")) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("AVP ", avp_code, " not found in ", pdu)); + } + return valueof(avp); +} + +function f_AVP_Grouped_get_avp(AVP_Grouped avp_grp, template (present) AVP_Code avp_code) +return template (omit) AVP +{ + var integer i; + + for (i := 0; i < lengthof(avp_grp); i := i+1) { + if (not ispresent(avp_grp[i].avp)) { + continue; + } + var AVP_Header hdr := avp_grp[i].avp.avp_header; + if (match(hdr.avp_code, avp_code)) { + return avp_grp[i].avp; + } + } + return omit; +} +function f_AVP_Grouped_get_avp_or_fail(AVP_Grouped avp_grp, template (present) AVP_Code avp_code) +return AVP +{ + var template (omit) AVP avp; + avp := f_AVP_Grouped_get_avp(avp_grp, avp_code); + if (istemplatekind(avp, "omit")) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("AVP ", avp_code, " not found in ", avp_grp)); + } + return valueof(avp); +} + }