diff --git a/epdg/EPDG_Tests.ttcn b/epdg/EPDG_Tests.ttcn index de5e7a854..82a23756b 100644 --- a/epdg/EPDG_Tests.ttcn +++ b/epdg/EPDG_Tests.ttcn @@ -359,7 +359,7 @@ private altstep as_DIA_SWx_MA_success() runs on EPDG_ConnHdlr { } /* Diameter SWx SAR + SAA. */ -private altstep as_DIA_SWx_SA_success() runs on EPDG_ConnHdlr { +private altstep as_DIA_SWx_SA_success(template (present) CxDx_3GPP_Server_Assignment_Type server_ass_type := ?) runs on EPDG_ConnHdlr { var PDU_DIAMETER rx_dia; var template (omit) AVP avp; var octetstring sess_id; @@ -404,7 +404,28 @@ private function f_S6b_AA_success() runs on EPDG_ConnHdlr { } } -/* Diameter SWx SAR + SAA. */ +/* Send STR as PGW to AAA server, expect back STA */ +private function f_S6b_ST_success() runs on EPDG_ConnHdlr { + var PDU_DIAMETER rx_dia; + var UINT32 hbh_id := f_rnd_octstring(4); + var UINT32 ete_id := f_rnd_octstring(4); + + /* Unlike STR, STA contains no IMSI. Register ete_id in DIAMETER_Emulation, + * so AIA is forwarded back to us in DIAMETER port instead of MTC_CT.DIAMETER_UNIT. + */ + f_epdg_connhldr_S6b_expect_eteid(ete_id); + + S6b.send(ts_DIA_S6b_STR(g_pars.imsi, DIAMETER_LOGOUT, + hbh_id := hbh_id, ete_id := ete_id)); + alt { + [] S6b.receive(tr_DIA_S6b_STA(DIAMETER_SUCCESS)) -> value rx_dia {} + [] S6b.receive(PDU_DIAMETER:?) -> value rx_dia { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected Diameter S6b msg rx: ", rx_dia)); + } + } +} + +/* ePDG Creates session at the PGW. PGW sends Diameter s6b AAR + AAA. */ private altstep as_GTP2C_CreateSession_success() runs on EPDG_ConnHdlr { var PDU_GTPCv2 rx_msg; var BearerContextIEs rx_bctx_ies; @@ -444,8 +465,27 @@ private altstep as_GTP2C_CreateSession_success() runs on EPDG_ConnHdlr { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected GTP2C msg rx: ", rx_msg)); } } -private function f_GTP2C_CreateSession_success() runs on EPDG_ConnHdlr { - as_GTP2C_CreateSession_success(); + +/* ePDG Deletes session at the PGW. PGW sends Diameter s6b AAR + AAA. */ +private altstep as_GTP2C_DeleteSession_success() runs on EPDG_ConnHdlr { + var PDU_GTPCv2 rx_msg; + var BearerContextIEs rx_bctx_ies; + var template (value) FullyQualifiedTEID fteid_c_ie, fteid_u_ie; + var template (value) PDN_AddressAllocation paa; + var template (value) BearerContextIEs bctx_ies; + + [] GTP2.receive(tr_GTP2C_DeleteSessionReq(g_pars.teic_local)) -> value rx_msg { + /* Upon rx of DeleteSession, emulate PGW requesting the AAA server for Sesssion Termination. */ + f_S6b_ST_success(); + + GTP2.send(ts_GTP2C_DeleteSessionResp(g_pars.teic_remote, + rx_msg.sequenceNumber, + Request_accepted)); + setverdict(pass); + } + [] GTP2.receive(PDU_GTPCv2:?) -> value rx_msg { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected GTP2C msg rx: ", rx_msg)); + } } /* Expect DeleteBearerResponse */ @@ -493,7 +533,7 @@ private function f_GSUP_LU_success() runs on EPDG_ConnHdlr { var GSUP_PDU rx_gsup; var template octetstring destination_name := *; GSUP.send(ts_GSUP_UL_REQ(g_pars.imsi)); - as_DIA_SWx_SA_success(); + as_DIA_SWx_SA_success(REGISTRATION); /* Expect a positive response back to the translator */ alt { [] GSUP.receive(tr_GSUP_UL_RES(g_pars.imsi, destination_name)); @@ -508,7 +548,7 @@ private function f_GSUP_LU_success() runs on EPDG_ConnHdlr { private function f_GSUP_EPDGTunnel_success() runs on EPDG_ConnHdlr { var GSUP_PDU rx_gsup; GSUP.send(ts_GSUP_EPDGTunnel_REQ(g_pars.imsi)); - f_GTP2C_CreateSession_success(); + as_GTP2C_CreateSession_success(); /* Expect a positive response back to the translator; */ var template (present) GSUP_IEs pdp_info := { tr_GSUP_IE_PDP_CONTEXT_ID(?), @@ -526,10 +566,30 @@ private function f_GSUP_EPDGTunnel_success() runs on EPDG_ConnHdlr { setverdict(pass); } +/* GSUP Purge MS Req + Resp, triggers S2b DeleteSession Req + Response. */ +private function f_GSUP_PurgeMS_success() runs on EPDG_ConnHdlr { + var GSUP_PDU rx_gsup; + GSUP.send(ts_GSUP_PURGE_MS_REQ(g_pars.imsi, OSMO_GSUP_CN_DOMAIN_PS)); + as_GTP2C_DeleteSession_success(); + /* ePDG internally sends STR to its AAA-Server. Since all sessions + become inactive, AAA-Server sends SAR(USER_DEREGISTRATION) to HSS: */ + + /* Expect a positive response back to the translator; */ + as_DIA_SWx_SA_success(USER_DEREGISTRATION); + alt { + [] GSUP.receive(tr_GSUP_PURGE_MS_RES(g_pars.imsi)); + [] GSUP.receive(GSUP_PDU:?) -> value rx_gsup { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected GSUP msg rx: ", rx_gsup)); + } + } + setverdict(pass); +} + private function f_TC_authinfo_normal(charstring id) runs on EPDG_ConnHdlr { f_GSUP_AI_success(); f_GSUP_LU_success(); f_GSUP_EPDGTunnel_success(); + f_GSUP_PurgeMS_success(); } testcase TC_authinfo_normal() runs on MTC_CT { diff --git a/library/DIAMETER_Templates.ttcn b/library/DIAMETER_Templates.ttcn index 82a433c26..98ca9bd55 100644 --- a/library/DIAMETER_Templates.ttcn +++ b/library/DIAMETER_Templates.ttcn @@ -307,6 +307,15 @@ template (present) GenericAVP tr_AVP_OriginStateId(template (present) OCTET4 sta } } +template (value) GenericAVP ts_AVP_TerminationCause(template (value) BASE_NONE_Termination_Cause tc) := { + avp := { + avp_header := ts_DIA_Hdr(c_AVP_Code_BASE_NONE_Termination_Cause), + avp_data := { + avp_BASE_NONE_Termination_Cause := tc + } + } +} + template (present) GenericAVP tr_AVP_TerminationCause(template (present) BASE_NONE_Termination_Cause tc) := { avp := { avp_header := tr_DIA_Hdr(c_AVP_Code_BASE_NONE_Termination_Cause), diff --git a/library/DIAMETER_ts29_273_Templates.ttcn b/library/DIAMETER_ts29_273_Templates.ttcn index 38355e70a..71f6cfc46 100644 --- a/library/DIAMETER_ts29_273_Templates.ttcn +++ b/library/DIAMETER_ts29_273_Templates.ttcn @@ -257,4 +257,55 @@ tr_DIA_S6b_AAA(template (present) octetstring sess_id := ?, tr_AVP_OriginRealm(orig_realm) )); + /* TS 29.273 9.2.2.3.1 Session-Termination-Request (STR) Command, + * Table 9.1.2.3.1/1: S6b Session Termination Request (STR), based on RFC 6733 8.4.1 */ +template (value) PDU_DIAMETER +ts_DIA_S6b_STR(template (value) hexstring imsi, + template (value) BASE_NONE_Termination_Cause term_cause := DIAMETER_LOGOUT, + template (value) octetstring sess_id := c_def_sess_id, + template (value) charstring orig_host := "pgw.localdomain", + template (value) charstring orig_realm := "localdomain", + template (value) charstring dest_realm := "localdomain", + template (value) UINT32 hbh_id := '00000000'O, + template (value) UINT32 ete_id := '00000000'O) := + ts_DIAMETER(flags := '11000000'B, + cmd_code := Session_Termination, + app_id := int2oct(c_DIAMETER_3GPP_S6b_AID, 4), + hbh_id := hbh_id, + ete_id := ete_id, + avps := { + ts_AVP_SessionId(sess_id), + /* Optional: DRMP */ + ts_AVP_AuthAppId(int2oct(c_DIAMETER_3GPP_S6b_AID, 4)), + ts_AVP_OriginHost(orig_host), + ts_AVP_OriginRealm(orig_realm), + ts_AVP_DestinationRealm(dest_realm), + ts_AVP_TerminationCause(term_cause), + ts_AVP_UserNameImsi(valueof(imsi)) + /* Optional: OC-Supported-Features */ + }); + +/* TS 29.273 9.2.2.3.2 Session-Termination-Answer (STA) Command, + * Table 9.1.2.3.1/2: S6b Session Termination Answer (STA), based on RFC 6733 8.4.2 */ +template (present) PDU_DIAMETER +tr_DIA_S6b_STA(template (present) DIAMETER_Resultcode res_code := ?, + template (present) octetstring sess_id := ?, + template (present) charstring orig_host := ?, + template (present) charstring orig_realm := ?, + template (present) charstring dest_realm := ?, + template (present) UINT32 hbh_id := ?, + template (present) UINT32 ete_id := ?) := + tr_DIAMETER(flags := '0???????'B, + cmd_code := Session_Termination, + app_id := int2oct(c_DIAMETER_3GPP_S6b_AID, 4), + hbh_id := hbh_id, ete_id := ete_id, + avps := superset( + tr_AVP_SessionId(sess_id), + /* Optional: DRMP */ + tr_AVP_ResultCode(res_code), + tr_AVP_OriginHost(orig_host), + tr_AVP_OriginRealm(orig_realm) + /* Lots other Optional */ + )); + }