pespin has submitted this change. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/43296?usp=email )
Change subject: pgw: SPlit PGW_Session_CT into its own file ......................................................................
pgw: SPlit PGW_Session_CT into its own file
Similar to what's already done in most testsuites.
Change-Id: I1b0d7be9e9655d407576bbaa905d9360840c5c21 --- A pgw/PGW_Session_CT.ttcn M pgw/PGW_Tests.ttcn 2 files changed, 570 insertions(+), 537 deletions(-)
Approvals: laforge: Looks good to me, but someone else must approve pespin: Looks good to me, approved Jenkins Builder: Verified
diff --git a/pgw/PGW_Session_CT.ttcn b/pgw/PGW_Session_CT.ttcn new file mode 100644 index 0000000..d45cad2 --- /dev/null +++ b/pgw/PGW_Session_CT.ttcn @@ -0,0 +1,568 @@ +module PGW_Session_CT { + +import from TCCEncoding_Functions all; + +import from General_Types all; +import from Osmocom_Types all; +import from Native_Functions all; +import from Misc_Helpers all; + +import from GTPv1U_Emulation all; + +import from GTPv2_Types all; +import from GTPv2_Templates all; +import from GTPv2_Emulation all; + +import from UECUPS_Types all; + +import from DNS_Helpers all; + + +import from DIAMETER_Types all; +import from DIAMETER_Templates all; +import from DIAMETER_ts29_212_Templates all; +import from DIAMETER_ts29_272_Templates all; +import from DIAMETER_ts29_273_Templates all; +import from DIAMETER_ts32_299_Templates all; +import from DIAMETER_Emulation all; + +import from PGW_Tests all; + +const integer DIA_Gx := 0; +const integer DIA_Gy := 1; +const integer DIA_S6b := 2; + +/* per-session component; we typically have 1..N per testcase */ +type component PGW_Session_CT extends GTP2_ConnHdlr, GTP1U_ConnHdlr, DIAMETER_ConnHdlr { + var SessionPars g_pars; + + /* GTP-U IPv4 address remote sie */ + var OCT4 g_gtpu4_remote; + var OCT16 g_gtpu6_remote; + + /* Address allocation */ + var OCT4 g_ip4_addr; + var OCT8 g_ip6_interface_id; + var OCT16 g_ip6_addr; + + /* Store last received Gy message */ + var PDU_DIAMETER g_rx_gy; + + /* number of programs started, used as identifier. */ + var integer g_start_prog_count := 0; +} + +type record BearerConfig { + /* EPS Bearer ID */ + uint4_t ebi optional, + /* TEI (Data) local side */ + OCT4 teid_local optional, + /* TEI (Data) remote side */ + OCT4 teid_remote optional +}; + +type record SessionParsGy { + /* In seconds. 0 => disabled, !0 => grant over CC-Time period */ + integer validity_time, + + /* Result-Code to use when sending Gy CCA, usually DIAMETER_SUCCESS */ + DIAMETER_Resultcode cca_res_code +}; + +/* configuration data for a given Session */ +type record SessionPars { + hexstring imsi, + hexstring msisdn optional, + // serving network + GTP2C_RAT_Type rat_type, + // flags? + charstring apn, + /* Apn subscribed or non-subscribed */ + boolean selection_mode, + GTP2C_PDN_Type pdn_type, + /* PAA */ + /* Max APN Restriction */ + /* APN-AMBR */ + octetstring pco optional, + octetstring epco optional, + + /* TEI (Control) local side */ + OCT4 teic_local, + /* TEI (Control) remote side */ + OCT4 teic_remote optional, + /* Bearer Contexts to be created */ + BearerConfig bearer optional, + + charstring tun_dev_name, + charstring tun_netns_name optional, + + SessionParsGy gy +} + +template (value) SessionPars +t_SessionPars(hexstring imsi, + charstring tundev, + template (omit) hexstring msisdn := '1234'H, + GTP2C_RAT_Type rat_type := GTP2C_RAT_EUTRAN, + charstring apn := "internet", + boolean selection_mode := false, + GTP2C_PDN_Type pdn_type := GTP2C_PDN_IPv4) := { + imsi := imsi, + msisdn := msisdn, + rat_type := rat_type, + apn := apn, + selection_mode := selection_mode, + pdn_type := pdn_type, + pco := omit, + epco := omit, + teic_local := '00000000'O, + teic_remote := omit, + bearer := { + ebi := 5, + teid_local := omit, + teid_remote := omit + }, + tun_dev_name := tundev, + tun_netns_name := tundev, + gy := { + validity_time := 0, + cca_res_code := DIAMETER_SUCCESS + } +} + + +type function void_fn() runs on PGW_Session_CT; + +function f_handler_init(void_fn fn, SessionPars pars) +runs on PGW_Session_CT { + g_pars := valueof(pars); + + if (DIAMETER_PROC[DIA_Gx].checkstate("Connected")) { + f_diameter_expect_imsi(g_pars.imsi, idx := DIA_Gx); + } + + if (DIAMETER_PROC[DIA_Gy].checkstate("Connected")) { + f_diameter_expect_imsi(g_pars.imsi, idx := DIA_Gy); + } + + if (DIAMETER_PROC[DIA_S6b].checkstate("Connected")) { + f_diameter_expect_imsi(g_pars.imsi, idx := DIA_S6b); + } + + /* allocate + register TEI{C,D} on local side */ + g_pars.teic_local := f_gtp2_allocate_teic(); + g_pars.bearer.teid_local := f_gtp1u_allocate_teid(); + fn.apply(); +} + +/* S6b emulation (AAA-Server) */ +private altstep as_DIA_S6b_AAR() runs on PGW_Session_CT { + var PDU_DIAMETER rx_dia; + [] DIAMETER[DIA_S6b].receive(tr_DIA_S6b_AAR()) -> value rx_dia { + var template (value) PDU_DIAMETER tx_dia; + tx_dia := f_ts_DIA_S6b_AAA(rx_dia, + "aaa." & mp_diam_realm, + mp_diam_realm, + mp_diam_realm); + DIAMETER[DIA_S6b].send(tx_dia); + setverdict(pass); + } + [] DIAMETER[DIA_S6b].receive(PDU_DIAMETER:?) -> value rx_dia { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Received unexpected DIAMETER ", rx_dia)); + } +} + +/* Gx emulation (PCRF)*/ +altstep as_DIA_Gx_CCR(DCC_NONE_CC_Request_Type req_type) runs on PGW_Session_CT { + var PDU_DIAMETER rx_dia; + [] DIAMETER[DIA_Gx].receive(tr_DIA_Gx_CCR(req_type := req_type)) -> value rx_dia { + var template (value) PDU_DIAMETER tx_dia := f_ts_DIA_Gx_CCA(rx_dia); + DIAMETER[DIA_Gx].send(tx_dia); + } + [] DIAMETER[DIA_Gx].receive(PDU_DIAMETER:?) -> value rx_dia { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Received unexpected DIAMETER ", rx_dia)); + } +} + +/* Gy emulation (OCS) */ +private function f_tr_DIA_Gy_CCR_sess(DCC_NONE_CC_Request_Type req_type) +runs on PGW_Session_CT return template (present) PDU_DIAMETER +{ + var octetstring imsi := char2oct(f_dec_TBCD(imsi_hex2oct(g_pars.imsi))); + var octetstring rat_type := int2oct(enum2int(g_pars.rat_type), 1); + var OCT1 nsapi := int2oct(g_pars.bearer.ebi, 1); + var OCT4 charging_char := char2oct(oct2str('0000'O)); // f_s5s8_create_session() uses hardcoded chg_car := '0000'O + var template (present) octetstring msisdn := ?; + var charstring smf_origin_host := "smf." & mp_diam_realm; + var octetstring sgsn_addr := f_inet_addr(mp_pcrf_local_ip); + var octetstring ggsn_addr := f_inet_addr(mp_pgw_hostname); + + return DIAMETER_ts32_299_Templates.f_tr_DIA_Gy_CCR(req_type, + imsi, + msisdn, + rat_type, + nsapi, + charging_char, + smf_origin_host, + mp_diam_realm, + sgsn_addr, + ggsn_addr); + +} + +altstep as_DIA_Gy_CCR(DCC_NONE_CC_Request_Type req_type) +runs on PGW_Session_CT { + [] DIAMETER[DIA_Gy].receive(f_tr_DIA_Gy_CCR_sess(req_type := req_type)) -> value g_rx_gy { + var template (value) PDU_DIAMETER tx_dia; + var template (omit) integer validity_time := omit; + if (g_pars.gy.validity_time > 0) { + validity_time := g_pars.gy.validity_time; + } + tx_dia := DIAMETER_ts32_299_Templates.f_ts_DIA_Gy_CCA(g_rx_gy, g_pars.gy.cca_res_code, validity_time); + DIAMETER[DIA_Gy].send(tx_dia); + } + [] DIAMETER[DIA_Gy].receive(PDU_DIAMETER:?) -> value g_rx_gy { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Received unexpected DIAMETER Gy", g_rx_gy)); + } +} + +/* GTPv2C */ +private function is_s2b_iface() runs on PGW_Session_CT return boolean { + return (g_pars.rat_type == GTP2C_RAT_WLAN or g_pars.rat_type == GTP2C_RAT_Virtual); +} + +/* find TEID of given interface type (and optionally instance) */ +private function f_find_teid(FullyQualifiedTEID_List list, + template (present) integer if_type, + template (present) BIT4 instance := ?) +return template (omit) FullyQualifiedTEID +{ + var integer i; + for (i := 0; i < lengthof(list); i := i+1) { + if (match(list[i].interfaceType, if_type) and + match(list[i].instance, instance)) { + return list[i]; + } + } + return omit; +} + +private function f_create_sess_tun() runs on PGW_Session_CT +{ + var template (value) UECUPS_AddrType user_addr_type; + var template (value) OCT4_20n user_addr; + + select (g_pars.pdn_type) { + case (GTP2C_PDN_IPv4) { + user_addr_type := IPV4; + user_addr := g_ip4_addr; + } + case (GTP2C_PDN_IPv6) { + user_addr_type := IPV6; + user_addr := g_ip6_addr; + } + case (GTP2C_PDN_IPv4v6) { + user_addr_type := IPV4V6; + user_addr := g_ip4_addr & g_ip6_addr; + } + } + + var template (value) UECUPS_CreateTun uecups_create := ts_UECUPS_CreateTun( + tx_teid := oct2int(g_pars.bearer.teid_remote), + rx_teid := oct2int(g_pars.bearer.teid_local), + user_addr_type := user_addr_type, + user_addr := user_addr, + local_gtp_ep := ts_UECUPS_SockAddr(f_inet_addr(mp_local_hostname_u)), + remote_gtp_ep := ts_UECUPS_SockAddr(g_gtpu4_remote), + tun_dev_name := g_pars.tun_dev_name, + tun_netns_name := g_pars.tun_netns_name + ); + + /* create tunnel in daemon */ + f_gtp1u_create_tunnel(uecups_create); +} + +private function f_perform_ipv6_slaac() runs on PGW_Session_CT +{ + var UECUPS_IPv6SLAACInd ipv6slaac_ind; + var template (value) UECUPS_IPv6SLAAC ipv6slaac; + ipv6slaac := ts_UECUPS_IPv6SLAAC( + local_gtp_ep := ts_UECUPS_SockAddr(f_inet_addr(mp_local_hostname_u)), + rx_teid := oct2int(g_pars.bearer.teid_local) + ); + f_gtp1u_ipv6_slaac(ipv6slaac); + GTP1U[0].receive(UECUPS_IPv6SLAACInd:?) -> value ipv6slaac_ind; + log("5GC assigned IPv6 global prefix: ", ipv6slaac_ind.ipv6_prefix, " -> ", f_inet6_ntoa(ipv6slaac_ind.ipv6_prefix & '0000000000000000'O)); + + /* Use the first global IPv6 address in the IPv6 prefix picked by osmo-uecups: */ + g_ip6_addr := ipv6slaac_ind.ipv6_user_addr; + log("Using IPv6 global address: ", g_ip6_addr); +} + +/* process one to-be-created bearer context */ +private function process_bctx_create(BearerContextGrouped bctx) runs on PGW_Session_CT +{ + g_pars.bearer.ebi := bctx.bearerContextIEs.ePS_Bearer_ID.ePS_Bearer_ID_Value; + /* FIXME: Cause */ + + var integer exp_fteid_if_type; + var BIT4 exp_fteid_instance; + if (is_s2b_iface()) { + exp_fteid_if_type := 33; + exp_fteid_instance := '0100'B; + } else { + exp_fteid_if_type := 5; + exp_fteid_instance := '0010'B; + } + + /* find F-TEID of the P-GW U side */ + var FullyQualifiedTEID rx_fteid; + rx_fteid := valueof(f_find_teid(bctx.bearerContextIEs.fullyQualifiedTEID, + exp_fteid_if_type, exp_fteid_instance)); + g_pars.bearer.teid_remote := rx_fteid.tEID_GRE_Key; + if (rx_fteid.v4_Flag == '1'B) { + g_gtpu4_remote := rx_fteid.iPv4_Address; + } + if (rx_fteid.v6_Flag == '1'B) { + g_gtpu6_remote := rx_fteid.iPv6_Address; + } + + f_create_sess_tun(); + if (g_pars.pdn_type == GTP2C_PDN_IPv6 or + g_pars.pdn_type == GTP2C_PDN_IPv4v6) { + f_perform_ipv6_slaac(); + } +} + +/* create a session on the PGW */ +private function f_create_session(template (value) FullyQualifiedTEID fteid_c_ie, + template (value) FullyQualifiedTEID fteid_u_ie, + template (omit) UserLocationInfo uli_ie := omit, + template (omit) APCO apco := omit, + template APCO exp_apco := *) runs on PGW_Session_CT { + var PDU_GTPCv2 rx; + + /* Defaults used for s5/s8: */ + var boolean do_s6b := false; + var template APN_Restriction apn_restriction := ?; + /* Change behavior when on S2b: */ + if (is_s2b_iface()) { + do_s6b := true; + apn_restriction := omit; + } + + var template (value) PDU_GTPCv2 g2c := + ts_GTP2C_CreateSessionReq(imsi := g_pars.imsi, + msisdn := g_pars.msisdn, + rat_type := enum2int(g_pars.rat_type), + sender_fteid := fteid_c_ie, + apn := f_enc_dns_hostname(g_pars.apn), + pdn_type := int2bit(enum2int(g_pars.pdn_type), 3), + teid_list := { fteid_u_ie }, + chg_car := '0000'O, + bearer_id := g_pars.bearer.ebi, + uli := uli_ie, + apco := apco); + g2c.gtpcv2_pdu.createSessionRequest.servingNetwork := ts_GTP2C_ServingNetwork('001'H, '01F'H); + + GTP2.send(g2c); + if (do_s6b and DIAMETER[DIA_S6b].checkstate("Connected")) { + as_DIA_S6b_AAR(); + } + if (DIAMETER[DIA_Gx].checkstate("Connected")) { + as_DIA_Gx_CCR(INITIAL_REQUEST); + } + /* FIXME: When on S2b interface, SMF is not using the Gy interface, unknown reason. */ + if (DIAMETER[DIA_Gy].checkstate("Connected")) { + as_DIA_Gy_CCR(INITIAL_REQUEST); + } + alt { + [] GTP2.receive(tr_GTP2C_CreateSessionResp(d_teid := g_pars.teic_local, + cause := Request_accepted, + apn_restriction := apn_restriction, + exp_apco := exp_apco)) -> value rx { + /* extract TEIDs */ + var CreateSessionResponse resp := rx.gtpcv2_pdu.createSessionResponse; + g_pars.teic_remote := resp.fullyQualifiedTEID[0].tEID_GRE_Key; + + /* extract allocated address[es] */ + var PDN_Address_and_Prefix paa := resp.pDN_AddressAllocation.pDN_Address_and_Prefix; + if (ischosen(paa.iPv4_Address)) { + g_ip4_addr := paa.iPv4_Address; + log("PGW assigned UE IPv4 address: ", f_inet_ntoa(g_ip4_addr)); + } else if (ischosen(paa.iPv6_Address)) { + if (paa.iPv6_Address.prefixLength != 64) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Got IPv6 Prefix Length ", + paa.iPv6_Address.prefixLength, " vs exp 64")); + } + g_ip6_interface_id := substr(paa.iPv6_Address.iPv6_Address, 8, 8); + log("PGW assigned IPv6 Interface Id: ", g_ip6_interface_id); + g_ip6_addr := 'FE80000000000000'O & g_ip6_interface_id; + log("PGW assigned IPv6 link-local Address: ", f_inet6_ntoa(g_ip6_addr)); + } else if (ischosen(paa.iPv4_IPv6)) { + if (paa.iPv4_IPv6.prefixLength != 64) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Got IPv6 Prefix Length ", + paa.iPv4_IPv6.prefixLength, " vs exp 64")); + } + g_ip4_addr := paa.iPv4_IPv6.iPv4_Address; + log("PGW assigned UE IPv4 address: ", f_inet_ntoa(g_ip4_addr)); + g_ip6_interface_id := substr(paa.iPv4_IPv6.iPv6_Address, 8, 8); + log("PGW assigned IPv6 Interface Id: ", g_ip6_interface_id); + g_ip6_addr := 'FE80000000000000'O & g_ip6_interface_id; + log("PGW assigned IPv6 link-local Address: ", f_inet6_ntoa(g_ip6_addr)); + } + var integer i; + for (i := 0; i < lengthof(resp.bearerContextGrouped); i := i+1) { + var BearerContextGrouped bctx := resp.bearerContextGrouped[i]; + select (bctx.instance) { + case ('0000'B) { // created + process_bctx_create(bctx); + } + case ('0001'B) { // removed + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + "We don't expect removed bearer contexts yet"); + } + } + } + } + [] GTP2.receive(tr_GTP2C_CreateSessionResp(d_teid:=g_pars.teic_local, cause:=?)) -> value rx { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Unexpected CreateSessionResp(cause=", + rx.gtpcv2_pdu.createSessionResponse.cause.causeValue, ")")); + } + [] GTP2.receive { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + "Unexpected GTPv2 while waiting for CreateSessionResp"); + } + } + +} + +/* create a session on the PGW on a S5/S8 interface (from SGW )*/ +function f_s5s8_create_session() runs on PGW_Session_CT { + var template (value) FullyQualifiedTEID fteid_c_ie, fteid_u_ie; + var template (value) UserLocationInfo uli_ie; + + fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPC, g_pars.teic_local, 0, + f_inet_addr(mp_local_hostname_c), omit); + fteid_u_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPU, g_pars.bearer.teid_local, 2, + f_inet_addr(mp_local_hostname_u), omit); + + + /* open5gs up to 1.2.3 won't accept it without ULI, despite not mandatory */ + var template (value) TAI tai := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0001'O }; + var template (value) ECGI ecgi := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0'H, 23 }; + uli_ie := ts_GTP2C_UserLocInfo(tai := tai, ecgi := ecgi); + + f_create_session(fteid_c_ie, fteid_u_ie, uli_ie); + +} + +/* create a session on the PGW on a S2b interface (from ePDG)*/ +function f_s2b_create_session(template (omit) APCO apco := omit, + template APCO exp_apco := *) runs on PGW_Session_CT { + var template (value) FullyQualifiedTEID fteid_c_ie, fteid_u_ie; + var template (value) UserLocationInfo uli_ie; + + fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S2b_ePDG_GTPC, g_pars.teic_local, 0, + f_inet_addr(mp_local_hostname_c), omit); + fteid_u_ie := ts_GTP2C_FTEID(FTEID_IF_S2bU_ePDG_GTPU, g_pars.bearer.teid_local, 5, + f_inet_addr(mp_local_hostname_u), omit); + + var template (value) TAI tai := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0001'O }; + var template (value) ECGI ecgi := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0'H, 23 }; + uli_ie := ts_GTP2C_UserLocInfo(tai := tai, ecgi := ecgi); + + f_create_session(fteid_c_ie, fteid_u_ie, uli_ie := uli_ie, apco := apco, exp_apco := exp_apco); + +} + +/* delete the session from the PGW */ +function f_delete_session(template (omit) GTP2C_Cause tx_cause := omit, + template (present) GTP2C_Cause exp_cause, + boolean expect_diameter := true) runs on PGW_Session_CT { + var template (value) FullyQualifiedTEID fteid_c_ie + fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPC, g_pars.teic_local, 0, + f_inet_addr(mp_local_hostname_c), omit); + var template PDU_GTPCv2 g2c := + ts_GTP2C_DeleteSessionReq(d_teid := g_pars.teic_remote, cause := tx_cause, + sender_fteid := fteid_c_ie, + teid_list := {}, bearer_id := g_pars.bearer.ebi); + + GTP2.send(g2c); + if (DIAMETER[DIA_Gx].checkstate("Connected") and expect_diameter) { + as_DIA_Gx_CCR(TERMINATION_REQUEST); + } + if (DIAMETER[DIA_Gy].checkstate("Connected") and expect_diameter) { + as_DIA_Gy_CCR(TERMINATION_REQUEST); + } + alt { + [] GTP2.receive(tr_GTP2C_DeleteSessionResp(d_teid := g_pars.teic_local, cause := exp_cause)) { + setverdict(pass); + } + [] GTP2.receive(tr_GTP2C_DeleteSessionResp(?, ?)) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + "Unexpected DeleteSessionResp"); + } + [] GTP2.receive { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + "Unexpected GTPv2 while waiting for DeleteSessionResp"); + } + } + + /* destroy tunnel in daemon */ + if (isbound(g_pars.bearer.teid_local)) { + var template (value) UECUPS_DestroyTun uecups_destroy; + uecups_destroy := ts_UECUPS_DestroyTun( + local_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(mp_local_hostname_u))), + rx_teid := oct2int(g_pars.bearer.teid_local) + ); + /* FIXME: what about IPv4/IPv6 differentiation? */ + f_gtp1u_destroy_tunnel(uecups_destroy); + } +} + + +private function f_run_prog_init(charstring command) runs on PGW_Session_CT return UECUPS_StartProgram +{ + var template (value) UECUPS_StartProgram sprog := ts_UECUPS_StartProgram( + command := command, + environment := {}, + run_as_user := mp_run_prog_as_user, + tun_netns_name := g_pars.tun_netns_name + ); + return valueof(sprog); +} + +private function f_run_prog_unique_log_path() runs on PGW_Session_CT return charstring +{ + var charstring id := testcasename() & "-" & hex2str(g_pars.imsi) & "-" & int2str(g_start_prog_count); + var charstring prefix := mp_run_prog_log_path & "/" & id; + g_start_prog_count := g_start_prog_count + 1; + return prefix; +} + +/* execute ping command and wait for result */ +function f_ping(charstring host, integer interval := 1, integer count := 10) runs on PGW_Session_CT +{ + /* command will be filled in by f_gtp1u_ping4() below: */ + var UECUPS_StartProgram sprog := f_run_prog_init(""); + var charstring bind_addr; + + if (f_addr_is_ipv6(host)) { + bind_addr := f_inet6_ntoa(g_ip6_addr); + } else { + bind_addr := f_inet_ntoa(g_ip4_addr); + } + + f_gtp1u_ping(sprog, host, interval, count, bind_addr, + redirect_output := true, + redirect_output_path_prefix := f_run_prog_unique_log_path()); +} + +} diff --git a/pgw/PGW_Tests.ttcn b/pgw/PGW_Tests.ttcn index 0c214fe..bd95090 100644 --- a/pgw/PGW_Tests.ttcn +++ b/pgw/PGW_Tests.ttcn @@ -26,6 +26,8 @@ import from DIAMETER_ts32_299_Templates all; import from DIAMETER_Emulation all;
+import from PGW_Session_CT all; + modulepar { charstring mp_pgw_hostname := "127.0.0.4"; charstring mp_local_hostname_c := "127.0.0.1"; @@ -48,10 +50,6 @@ charstring mp_diam_realm := "localdomain"; }
-const integer DIA_Gx := 0; -const integer DIA_Gy := 1; -const integer DIA_S6b := 2; - /* main component, we typically have one per testcase */ type component PGW_Test_CT extends GTP2_ConnHdlr, GTP1U_ConnHdlr { var GTPv2_Emulation_CT vc_GTP2; @@ -83,107 +81,6 @@ } }
-/* per-session component; we typically have 1..N per testcase */ -type component PGW_Session_CT extends GTP2_ConnHdlr, GTP1U_ConnHdlr, DIAMETER_ConnHdlr { - var SessionPars g_pars; - - /* GTP-U IPv4 address remote sie */ - var OCT4 g_gtpu4_remote; - var OCT16 g_gtpu6_remote; - - /* Address allocation */ - var OCT4 g_ip4_addr; - var OCT8 g_ip6_interface_id; - var OCT16 g_ip6_addr; - - /* Store last received Gy message */ - var PDU_DIAMETER g_rx_gy; - - /* number of programs started, used as identifier. */ - var integer g_start_prog_count := 0; -} - -type record BearerConfig { - /* EPS Bearer ID */ - uint4_t ebi optional, - /* TEI (Data) local side */ - OCT4 teid_local optional, - /* TEI (Data) remote side */ - OCT4 teid_remote optional -}; - -type record SessionParsGy { - /* In seconds. 0 => disabled, !0 => grant over CC-Time period */ - integer validity_time, - - /* Result-Code to use when sending Gy CCA, usually DIAMETER_SUCCESS */ - DIAMETER_Resultcode cca_res_code -}; - -/* configuration data for a given Session */ -type record SessionPars { - hexstring imsi, - hexstring msisdn optional, - // serving network - GTP2C_RAT_Type rat_type, - // flags? - charstring apn, - /* Apn subscribed or non-subscribed */ - boolean selection_mode, - GTP2C_PDN_Type pdn_type, - /* PAA */ - /* Max APN Restriction */ - /* APN-AMBR */ - octetstring pco optional, - octetstring epco optional, - - /* TEI (Control) local side */ - OCT4 teic_local, - /* TEI (Control) remote side */ - OCT4 teic_remote optional, - /* Bearer Contexts to be created */ - BearerConfig bearer optional, - - charstring tun_dev_name, - charstring tun_netns_name optional, - - SessionParsGy gy -} - -template (value) SessionPars -t_SessionPars(hexstring imsi, - charstring tundev, - template (omit) hexstring msisdn := '1234'H, - GTP2C_RAT_Type rat_type := GTP2C_RAT_EUTRAN, - charstring apn := "internet", - boolean selection_mode := false, - GTP2C_PDN_Type pdn_type := GTP2C_PDN_IPv4) := { - imsi := imsi, - msisdn := msisdn, - rat_type := rat_type, - apn := apn, - selection_mode := selection_mode, - pdn_type := pdn_type, - pco := omit, - epco := omit, - teic_local := '00000000'O, - teic_remote := omit, - bearer := { - ebi := 5, - teid_local := omit, - teid_remote := omit - }, - tun_dev_name := tundev, - tun_netns_name := tundev, - gy := { - validity_time := 0, - cca_res_code := DIAMETER_SUCCESS - } -} - - -type function void_fn() runs on PGW_Session_CT; - friend function DiameterForwardUnitdataCallback(PDU_DIAMETER msg) runs on DIAMETER_Emulation_CT return template PDU_DIAMETER { DIAMETER_UNIT.send(msg); @@ -341,438 +238,6 @@ return vc_conn; }
-private function f_handler_init(void_fn fn, SessionPars pars) -runs on PGW_Session_CT { - g_pars := valueof(pars); - - if (DIAMETER_PROC[DIA_Gx].checkstate("Connected")) { - f_diameter_expect_imsi(g_pars.imsi, idx := DIA_Gx); - } - - if (DIAMETER_PROC[DIA_Gy].checkstate("Connected")) { - f_diameter_expect_imsi(g_pars.imsi, idx := DIA_Gy); - } - - if (DIAMETER_PROC[DIA_S6b].checkstate("Connected")) { - f_diameter_expect_imsi(g_pars.imsi, idx := DIA_S6b); - } - - /* allocate + register TEI{C,D} on local side */ - g_pars.teic_local := f_gtp2_allocate_teic(); - g_pars.bearer.teid_local := f_gtp1u_allocate_teid(); - fn.apply(); -} - -/* S6b emulation (AAA-Server) */ -private altstep as_DIA_S6b_AAR() runs on PGW_Session_CT { - var PDU_DIAMETER rx_dia; - [] DIAMETER[DIA_S6b].receive(tr_DIA_S6b_AAR()) -> value rx_dia { - var template (value) PDU_DIAMETER tx_dia; - tx_dia := f_ts_DIA_S6b_AAA(rx_dia, - "aaa." & mp_diam_realm, - mp_diam_realm, - mp_diam_realm); - DIAMETER[DIA_S6b].send(tx_dia); - setverdict(pass); - } - [] DIAMETER[DIA_S6b].receive(PDU_DIAMETER:?) -> value rx_dia { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, - log2str("Received unexpected DIAMETER ", rx_dia)); - } -} - -/* Gx emulation (PCRF)*/ -private altstep as_DIA_Gx_CCR(DCC_NONE_CC_Request_Type req_type) runs on PGW_Session_CT { - var PDU_DIAMETER rx_dia; - [] DIAMETER[DIA_Gx].receive(tr_DIA_Gx_CCR(req_type := req_type)) -> value rx_dia { - var template (value) PDU_DIAMETER tx_dia := f_ts_DIA_Gx_CCA(rx_dia); - DIAMETER[DIA_Gx].send(tx_dia); - } - [] DIAMETER[DIA_Gx].receive(PDU_DIAMETER:?) -> value rx_dia { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, - log2str("Received unexpected DIAMETER ", rx_dia)); - } -} - -/* Gy emulation (OCS) */ -function f_tr_DIA_Gy_CCR(DCC_NONE_CC_Request_Type req_type) -runs on PGW_Session_CT return template (present) PDU_DIAMETER -{ - var octetstring imsi := char2oct(f_dec_TBCD(imsi_hex2oct(g_pars.imsi))); - var octetstring rat_type := int2oct(enum2int(g_pars.rat_type), 1); - var OCT1 nsapi := int2oct(g_pars.bearer.ebi, 1); - var OCT4 charging_char := char2oct(oct2str('0000'O)); // f_s5s8_create_session() uses hardcoded chg_car := '0000'O - var template (present) octetstring msisdn := ?; - var charstring smf_origin_host := "smf." & mp_diam_realm; - var octetstring sgsn_addr := f_inet_addr(mp_pcrf_local_ip); - var octetstring ggsn_addr := f_inet_addr(mp_pgw_hostname); - - return DIAMETER_ts32_299_Templates.f_tr_DIA_Gy_CCR(req_type, - imsi, - msisdn, - rat_type, - nsapi, - charging_char, - smf_origin_host, - mp_diam_realm, - sgsn_addr, - ggsn_addr); - -} - -private altstep as_DIA_Gy_CCR(DCC_NONE_CC_Request_Type req_type) -runs on PGW_Session_CT { - [] DIAMETER[DIA_Gy].receive(f_tr_DIA_Gy_CCR(req_type := req_type)) -> value g_rx_gy { - var template (value) PDU_DIAMETER tx_dia; - var template (omit) integer validity_time := omit; - if (g_pars.gy.validity_time > 0) { - validity_time := g_pars.gy.validity_time; - } - tx_dia := DIAMETER_ts32_299_Templates.f_ts_DIA_Gy_CCA(g_rx_gy, g_pars.gy.cca_res_code, validity_time); - DIAMETER[DIA_Gy].send(tx_dia); - } - [] DIAMETER[DIA_Gy].receive(PDU_DIAMETER:?) -> value g_rx_gy { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, - log2str("Received unexpected DIAMETER Gy", g_rx_gy)); - } -} - -/* GTPv2C */ -private function is_s2b_iface() runs on PGW_Session_CT return boolean { - return (g_pars.rat_type == GTP2C_RAT_WLAN or g_pars.rat_type == GTP2C_RAT_Virtual); -} - -/* find TEID of given interface type (and optionally instance) */ -private function f_find_teid(FullyQualifiedTEID_List list, - template (present) integer if_type, - template (present) BIT4 instance := ?) -return template (omit) FullyQualifiedTEID -{ - var integer i; - for (i := 0; i < lengthof(list); i := i+1) { - if (match(list[i].interfaceType, if_type) and - match(list[i].instance, instance)) { - return list[i]; - } - } - return omit; -} - -private function f_create_sess_tun() runs on PGW_Session_CT -{ - var template (value) UECUPS_AddrType user_addr_type; - var template (value) OCT4_20n user_addr; - - select (g_pars.pdn_type) { - case (GTP2C_PDN_IPv4) { - user_addr_type := IPV4; - user_addr := g_ip4_addr; - } - case (GTP2C_PDN_IPv6) { - user_addr_type := IPV6; - user_addr := g_ip6_addr; - } - case (GTP2C_PDN_IPv4v6) { - user_addr_type := IPV4V6; - user_addr := g_ip4_addr & g_ip6_addr; - } - } - - var template (value) UECUPS_CreateTun uecups_create := ts_UECUPS_CreateTun( - tx_teid := oct2int(g_pars.bearer.teid_remote), - rx_teid := oct2int(g_pars.bearer.teid_local), - user_addr_type := user_addr_type, - user_addr := user_addr, - local_gtp_ep := ts_UECUPS_SockAddr(f_inet_addr(mp_local_hostname_u)), - remote_gtp_ep := ts_UECUPS_SockAddr(g_gtpu4_remote), - tun_dev_name := g_pars.tun_dev_name, - tun_netns_name := g_pars.tun_netns_name - ); - - /* create tunnel in daemon */ - f_gtp1u_create_tunnel(uecups_create); -} - -private function f_perform_ipv6_slaac() runs on PGW_Session_CT -{ - var UECUPS_IPv6SLAACInd ipv6slaac_ind; - var template (value) UECUPS_IPv6SLAAC ipv6slaac; - ipv6slaac := ts_UECUPS_IPv6SLAAC( - local_gtp_ep := ts_UECUPS_SockAddr(f_inet_addr(mp_local_hostname_u)), - rx_teid := oct2int(g_pars.bearer.teid_local) - ); - f_gtp1u_ipv6_slaac(ipv6slaac); - GTP1U[0].receive(UECUPS_IPv6SLAACInd:?) -> value ipv6slaac_ind; - log("5GC assigned IPv6 global prefix: ", ipv6slaac_ind.ipv6_prefix, " -> ", f_inet6_ntoa(ipv6slaac_ind.ipv6_prefix & '0000000000000000'O)); - - /* Use the first global IPv6 address in the IPv6 prefix picked by osmo-uecups: */ - g_ip6_addr := ipv6slaac_ind.ipv6_user_addr; - log("Using IPv6 global address: ", g_ip6_addr); -} - -/* process one to-be-created bearer context */ -private function process_bctx_create(BearerContextGrouped bctx) runs on PGW_Session_CT -{ - g_pars.bearer.ebi := bctx.bearerContextIEs.ePS_Bearer_ID.ePS_Bearer_ID_Value; - /* FIXME: Cause */ - - var integer exp_fteid_if_type; - var BIT4 exp_fteid_instance; - if (is_s2b_iface()) { - exp_fteid_if_type := 33; - exp_fteid_instance := '0100'B; - } else { - exp_fteid_if_type := 5; - exp_fteid_instance := '0010'B; - } - - /* find F-TEID of the P-GW U side */ - var FullyQualifiedTEID rx_fteid; - rx_fteid := valueof(f_find_teid(bctx.bearerContextIEs.fullyQualifiedTEID, - exp_fteid_if_type, exp_fteid_instance)); - g_pars.bearer.teid_remote := rx_fteid.tEID_GRE_Key; - if (rx_fteid.v4_Flag == '1'B) { - g_gtpu4_remote := rx_fteid.iPv4_Address; - } - if (rx_fteid.v6_Flag == '1'B) { - g_gtpu6_remote := rx_fteid.iPv6_Address; - } - - f_create_sess_tun(); - if (g_pars.pdn_type == GTP2C_PDN_IPv6 or - g_pars.pdn_type == GTP2C_PDN_IPv4v6) { - f_perform_ipv6_slaac(); - } -} - -/* create a session on the PGW */ -private function f_create_session(template (value) FullyQualifiedTEID fteid_c_ie, - template (value) FullyQualifiedTEID fteid_u_ie, - template (omit) UserLocationInfo uli_ie := omit, - template (omit) APCO apco := omit, - template APCO exp_apco := *) runs on PGW_Session_CT { - var PDU_GTPCv2 rx; - - /* Defaults used for s5/s8: */ - var boolean do_s6b := false; - var template APN_Restriction apn_restriction := ?; - /* Change behavior when on S2b: */ - if (is_s2b_iface()) { - do_s6b := true; - apn_restriction := omit; - } - - var template (value) PDU_GTPCv2 g2c := - ts_GTP2C_CreateSessionReq(imsi := g_pars.imsi, - msisdn := g_pars.msisdn, - rat_type := enum2int(g_pars.rat_type), - sender_fteid := fteid_c_ie, - apn := f_enc_dns_hostname(g_pars.apn), - pdn_type := int2bit(enum2int(g_pars.pdn_type), 3), - teid_list := { fteid_u_ie }, - chg_car := '0000'O, - bearer_id := g_pars.bearer.ebi, - uli := uli_ie, - apco := apco); - g2c.gtpcv2_pdu.createSessionRequest.servingNetwork := ts_GTP2C_ServingNetwork('001'H, '01F'H); - - GTP2.send(g2c); - if (do_s6b and DIAMETER[DIA_S6b].checkstate("Connected")) { - as_DIA_S6b_AAR(); - } - if (DIAMETER[DIA_Gx].checkstate("Connected")) { - as_DIA_Gx_CCR(INITIAL_REQUEST); - } - /* FIXME: When on S2b interface, SMF is not using the Gy interface, unknown reason. */ - if (DIAMETER[DIA_Gy].checkstate("Connected")) { - as_DIA_Gy_CCR(INITIAL_REQUEST); - } - alt { - [] GTP2.receive(tr_GTP2C_CreateSessionResp(d_teid := g_pars.teic_local, - cause := Request_accepted, - apn_restriction := apn_restriction, - exp_apco := exp_apco)) -> value rx { - /* extract TEIDs */ - var CreateSessionResponse resp := rx.gtpcv2_pdu.createSessionResponse; - g_pars.teic_remote := resp.fullyQualifiedTEID[0].tEID_GRE_Key; - - /* extract allocated address[es] */ - var PDN_Address_and_Prefix paa := resp.pDN_AddressAllocation.pDN_Address_and_Prefix; - if (ischosen(paa.iPv4_Address)) { - g_ip4_addr := paa.iPv4_Address; - log("PGW assigned UE IPv4 address: ", f_inet_ntoa(g_ip4_addr)); - } else if (ischosen(paa.iPv6_Address)) { - if (paa.iPv6_Address.prefixLength != 64) { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, - log2str("Got IPv6 Prefix Length ", - paa.iPv6_Address.prefixLength, " vs exp 64")); - } - g_ip6_interface_id := substr(paa.iPv6_Address.iPv6_Address, 8, 8); - log("PGW assigned IPv6 Interface Id: ", g_ip6_interface_id); - g_ip6_addr := 'FE80000000000000'O & g_ip6_interface_id; - log("PGW assigned IPv6 link-local Address: ", f_inet6_ntoa(g_ip6_addr)); - } else if (ischosen(paa.iPv4_IPv6)) { - if (paa.iPv4_IPv6.prefixLength != 64) { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, - log2str("Got IPv6 Prefix Length ", - paa.iPv4_IPv6.prefixLength, " vs exp 64")); - } - g_ip4_addr := paa.iPv4_IPv6.iPv4_Address; - log("PGW assigned UE IPv4 address: ", f_inet_ntoa(g_ip4_addr)); - g_ip6_interface_id := substr(paa.iPv4_IPv6.iPv6_Address, 8, 8); - log("PGW assigned IPv6 Interface Id: ", g_ip6_interface_id); - g_ip6_addr := 'FE80000000000000'O & g_ip6_interface_id; - log("PGW assigned IPv6 link-local Address: ", f_inet6_ntoa(g_ip6_addr)); - } - var integer i; - for (i := 0; i < lengthof(resp.bearerContextGrouped); i := i+1) { - var BearerContextGrouped bctx := resp.bearerContextGrouped[i]; - select (bctx.instance) { - case ('0000'B) { // created - process_bctx_create(bctx); - } - case ('0001'B) { // removed - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, - "We don't expect removed bearer contexts yet"); - } - } - } - } - [] GTP2.receive(tr_GTP2C_CreateSessionResp(d_teid:=g_pars.teic_local, cause:=?)) -> value rx { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, - log2str("Unexpected CreateSessionResp(cause=", - rx.gtpcv2_pdu.createSessionResponse.cause.causeValue, ")")); - } - [] GTP2.receive { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, - "Unexpected GTPv2 while waiting for CreateSessionResp"); - } - } - -} - -/* create a session on the PGW on a S5/S8 interface (from SGW )*/ -private function f_s5s8_create_session() runs on PGW_Session_CT { - var template (value) FullyQualifiedTEID fteid_c_ie, fteid_u_ie; - var template (value) UserLocationInfo uli_ie; - - fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPC, g_pars.teic_local, 0, - f_inet_addr(mp_local_hostname_c), omit); - fteid_u_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPU, g_pars.bearer.teid_local, 2, - f_inet_addr(mp_local_hostname_u), omit); - - - /* open5gs up to 1.2.3 won't accept it without ULI, despite not mandatory */ - var template (value) TAI tai := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0001'O }; - var template (value) ECGI ecgi := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0'H, 23 }; - uli_ie := ts_GTP2C_UserLocInfo(tai := tai, ecgi := ecgi); - - f_create_session(fteid_c_ie, fteid_u_ie, uli_ie); - -} - -/* create a session on the PGW on a S2b interface (from ePDG)*/ -private function f_s2b_create_session(template (omit) APCO apco := omit, - template APCO exp_apco := *) runs on PGW_Session_CT { - var template (value) FullyQualifiedTEID fteid_c_ie, fteid_u_ie; - var template (value) UserLocationInfo uli_ie; - - fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S2b_ePDG_GTPC, g_pars.teic_local, 0, - f_inet_addr(mp_local_hostname_c), omit); - fteid_u_ie := ts_GTP2C_FTEID(FTEID_IF_S2bU_ePDG_GTPU, g_pars.bearer.teid_local, 5, - f_inet_addr(mp_local_hostname_u), omit); - - var template (value) TAI tai := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0001'O }; - var template (value) ECGI ecgi := { '0'H, '0'H, '1'H, 'F'H, '0'H, '1'H, '0'H, 23 }; - uli_ie := ts_GTP2C_UserLocInfo(tai := tai, ecgi := ecgi); - - f_create_session(fteid_c_ie, fteid_u_ie, uli_ie := uli_ie, apco := apco, exp_apco := exp_apco); - -} - -/* delete the session from the PGW */ -private function f_delete_session(template (omit) GTP2C_Cause tx_cause := omit, - template (present) GTP2C_Cause exp_cause, - boolean expect_diameter := true) runs on PGW_Session_CT { - var template (value) FullyQualifiedTEID fteid_c_ie - fteid_c_ie := ts_GTP2C_FTEID(FTEID_IF_S5S8_SGW_GTPC, g_pars.teic_local, 0, - f_inet_addr(mp_local_hostname_c), omit); - var template PDU_GTPCv2 g2c := - ts_GTP2C_DeleteSessionReq(d_teid := g_pars.teic_remote, cause := tx_cause, - sender_fteid := fteid_c_ie, - teid_list := {}, bearer_id := g_pars.bearer.ebi); - - GTP2.send(g2c); - if (DIAMETER[DIA_Gx].checkstate("Connected") and expect_diameter) { - as_DIA_Gx_CCR(TERMINATION_REQUEST); - } - if (DIAMETER[DIA_Gy].checkstate("Connected") and expect_diameter) { - as_DIA_Gy_CCR(TERMINATION_REQUEST); - } - alt { - [] GTP2.receive(tr_GTP2C_DeleteSessionResp(d_teid := g_pars.teic_local, cause := exp_cause)) { - setverdict(pass); - } - [] GTP2.receive(tr_GTP2C_DeleteSessionResp(?, ?)) { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, - "Unexpected DeleteSessionResp"); - } - [] GTP2.receive { - Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, - "Unexpected GTPv2 while waiting for DeleteSessionResp"); - } - } - - /* destroy tunnel in daemon */ - if (isbound(g_pars.bearer.teid_local)) { - var template (value) UECUPS_DestroyTun uecups_destroy; - uecups_destroy := ts_UECUPS_DestroyTun( - local_gtp_ep := valueof(ts_UECUPS_SockAddr(f_inet_addr(mp_local_hostname_u))), - rx_teid := oct2int(g_pars.bearer.teid_local) - ); - /* FIXME: what about IPv4/IPv6 differentiation? */ - f_gtp1u_destroy_tunnel(uecups_destroy); - } -} - - -private function f_run_prog_init(charstring command) runs on PGW_Session_CT return UECUPS_StartProgram -{ - var template (value) UECUPS_StartProgram sprog := ts_UECUPS_StartProgram( - command := command, - environment := {}, - run_as_user := mp_run_prog_as_user, - tun_netns_name := g_pars.tun_netns_name - ); - return valueof(sprog); -} - -private function f_run_prog_unique_log_path() runs on PGW_Session_CT return charstring -{ - var charstring id := testcasename() & "-" & hex2str(g_pars.imsi) & "-" & int2str(g_start_prog_count); - var charstring prefix := mp_run_prog_log_path & "/" & id; - g_start_prog_count := g_start_prog_count + 1; - return prefix; -} - -/* execute ping command and wait for result */ -private function f_ping(charstring host, integer interval := 1, integer count := 10) runs on PGW_Session_CT -{ - /* command will be filled in by f_gtp1u_ping4() below: */ - var UECUPS_StartProgram sprog := f_run_prog_init(""); - var charstring bind_addr; - - if (f_addr_is_ipv6(host)) { - bind_addr := f_inet6_ntoa(g_ip6_addr); - } else { - bind_addr := f_inet_ntoa(g_ip4_addr); - } - - f_gtp1u_ping(sprog, host, interval, count, bind_addr, - redirect_output := true, - redirect_output_path_prefix := f_run_prog_unique_log_path()); -} - /* send echo request; expect response */ testcase TC_tx_echo() runs on PGW_Test_CT { timer T := 5.0;