diff options
author | Pau Espin Pedrol <pespin@sysmocom.de> | 2024-02-23 17:45:08 +0100 |
---|---|---|
committer | pespin <pespin@sysmocom.de> | 2024-02-28 12:54:21 +0000 |
commit | 518e24b861222d75ab071c011630b1be5e4eb884 (patch) | |
tree | 1dd0e25fe094db9b944c5552e72b328cf7fbf5c2 | |
parent | 4199a701c36fa3cb1c688f3c10cae69f715ab8bc (diff) |
pgw: Introduce test TC_s2b_createSession_v4_no_apco
This test allows initial testing of a session creation through the S2b
interface (emulating an ePDG).
A follow-up test will be added to test the APCO IE (feature which
open5gs-smfd still doesn't support).
Change-Id: I38e469edf0e00feca5a648035b64645e2c905937
-rw-r--r-- | library/DIAMETER_Emulation.ttcn | 9 | ||||
-rw-r--r-- | library/DIAMETER_ts29_273_Templates.ttcn | 50 | ||||
-rw-r--r-- | library/GTPv2_Templates.ttcn | 17 | ||||
-rw-r--r-- | library/Osmocom_Types.ttcn | 9 | ||||
-rw-r--r-- | pgw/PGW_Tests.ttcn | 146 | ||||
-rw-r--r-- | pgw/expected-results.xml | 1 | ||||
-rwxr-xr-x | pgw/gen_links.sh | 2 |
7 files changed, 222 insertions, 12 deletions
diff --git a/library/DIAMETER_Emulation.ttcn b/library/DIAMETER_Emulation.ttcn index e7481ca9..69107d4b 100644 --- a/library/DIAMETER_Emulation.ttcn +++ b/library/DIAMETER_Emulation.ttcn @@ -37,6 +37,7 @@ import from DIAMETER_Types all; import from DIAMETER_Templates all; import from Osmocom_Types all; import from IPL4asp_Types all; +import from TCCConversion_Functions all; import from Native_Functions all; type hexstring IMSI; @@ -297,7 +298,13 @@ function f_DIAMETER_get_imsi(PDU_DIAMETER pdu) return template (omit) IMSI return str2hex(oct2char(grp[1].avp.avp_data.avp_DCC_NONE_Subscription_Id_Data)); } else { var octetstring imsi_oct := valueof(imsi_avp.avp_data.avp_BASE_NONE_User_Name); - return str2hex(oct2char(imsi_oct)); + var charstring imsi_str := oct2char(imsi_oct); + /* Username may be a NAI instead of IMSI: "<IMSI>@nai.epc.mnc<MNC>.mcc<MCC>.3gppnetwork.org" */ + var integer pos := f_strstr(imsi_str, "@"); + if (pos != -1) { + imsi_str := substr(imsi_str, 0, pos); + } + return str2hex(imsi_str); } } diff --git a/library/DIAMETER_ts29_273_Templates.ttcn b/library/DIAMETER_ts29_273_Templates.ttcn index e3a6c2fe..87d16011 100644 --- a/library/DIAMETER_ts29_273_Templates.ttcn +++ b/library/DIAMETER_ts29_273_Templates.ttcn @@ -204,6 +204,35 @@ ts_DIA_SWx_SAA(template (value) hexstring imsi, *******************************/ /* TS 29.273 9.2.2.5.1 AA-Request (AAR) */ +template (present) PDU_DIAMETER +tr_DIA_S6b_AAR(template (present) charstring username_nai := ?, + template (present) MIPv6_NONE_MIP6_Feature_Vector mip6_feat_vec := ?, + template (present) charstring apn := ?, + 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 := '1???????'B, + cmd_code := Authorize_Authenticate, + 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_AuthAppId(int2oct(c_DIAMETER_3GPP_S6b_AID, 4)), + tr_AVP_OriginHost(orig_host), + tr_AVP_OriginRealm(orig_realm), + tr_AVP_DestinationRealm(dest_realm), + tr_AVP_AuthRequestType(AUTHORIZE_ONLY), + tr_AVP_UserName(char2oct_tmpl_present(username_nai)), + tr_AVP_MIP6FeatureVector(mip6_feat_vec), + tr_AVP_ServiceSelection(apn) + /* TODO: Lots other optional */ + )); + template (value) PDU_DIAMETER ts_DIA_S6b_AAR(template (value) charstring username_nai, template (value) MIPv6_NONE_MIP6_Feature_Vector mip6_feat_vec, @@ -257,6 +286,27 @@ tr_DIA_S6b_AAA(template (present) octetstring sess_id := ?, tr_AVP_OriginRealm(orig_realm) )); +template (value) PDU_DIAMETER +ts_DIA_S6b_AAA(template (value) octetstring sess_id := c_def_sess_id, + template (value) charstring orig_host := "aaa.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 := '01000000'B, + cmd_code := Authorize_Authenticate, + 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_AuthRequestType(AUTHORIZE_ONLY), + ts_AVP_ResultCode(DIAMETER_SUCCESS), + ts_AVP_OriginHost(orig_host), + ts_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 diff --git a/library/GTPv2_Templates.ttcn b/library/GTPv2_Templates.ttcn index 431415a5..c8422307 100644 --- a/library/GTPv2_Templates.ttcn +++ b/library/GTPv2_Templates.ttcn @@ -407,6 +407,20 @@ ts_GTP2C_BearerQos(template (value) OCT1 qci, } /* 8.17 */ +type enumerated GTP2C_RAT_Type { + GTP2C_RAT_reserved ('00'O), + GTP2C_RAT_UTRAN ('01'O), + GTP2C_RAT_GERAN ('02'O), + GTP2C_RAT_WLAN ('03'O), + GTP2C_RAT_GAN ('04'O), + GTP2C_RAT_HSPA_EVOLUTION ('05'O), + GTP2C_RAT_EUTRAN ('06'O), + GTP2C_RAT_Virtual ('07'O), + GTP2C_RAT_EUTRAN_NB_IoT ('08'O), + GTP2C_RAT_LTE_M ('09'O), + GTP2C_RAT_NR ('0A'O) +} with { variant "FIELDLENGTH(8)" encode "RAW" }; + template (value) RAT_Type ts_GTP2C_RatType(template (value) integer rat) := { elementIdentifier := '52'O, lengthIndicator := 0, /* overwritten */ @@ -1046,6 +1060,7 @@ tr_GTP2C_CreateSessionResp(template (present) OCT4 d_teid := ?, template (present) GTP2C_Cause cause := ?, template FullyQualifiedTEID_List fteids := *, template PDN_AddressAllocation addr := *, + template APN_Restriction apn_restriction := *, template BearerContextGrouped_List bctxg := *) := tr_PDU_GTP2C(d_teid, seq, { createSessionResponse := { @@ -1055,7 +1070,7 @@ tr_PDU_GTP2C(d_teid, seq, { heNBInformationReporting := *, fullyQualifiedTEID := fteids, pDN_AddressAllocation := addr, - aPN_Restriction := ?, + aPN_Restriction := apn_restriction, ambr := *, linkedEPS_Bearer_ID := *, protocolConfigOptions := *, diff --git a/library/Osmocom_Types.ttcn b/library/Osmocom_Types.ttcn index d034a74e..66534086 100644 --- a/library/Osmocom_Types.ttcn +++ b/library/Osmocom_Types.ttcn @@ -179,6 +179,15 @@ function char2oct_tmpl(template charstring inp) return template octetstring } } +function char2oct_tmpl_present(template (present) charstring inp) return template (present) octetstring +{ + if (istemplatekind(inp, "?")) { + return ?; + } else { + return char2oct(valueof(inp)); + } +} + function hex2str_tmpl(template hexstring inp) return template charstring { if (istemplatekind(inp, "omit")) { diff --git a/pgw/PGW_Tests.ttcn b/pgw/PGW_Tests.ttcn index bd32760f..e39bd629 100644 --- a/pgw/PGW_Tests.ttcn +++ b/pgw/PGW_Tests.ttcn @@ -20,6 +20,7 @@ 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; @@ -39,6 +40,9 @@ modulepar { charstring mp_ocs_local_ip := "127.0.0.9"; integer mp_ocs_local_port := 3869; + charstring mp_aaa_local_ip := "127.0.0.9"; + integer mp_aaa_local_port := 3870; + charstring mp_diam_realm := "localdomain"; } @@ -55,6 +59,10 @@ type component PGW_Test_CT extends GTP2_ConnHdlr { var DIAMETER_Emulation_CT vc_Gy; port DIAMETER_PT Gy_UNIT; port DIAMETEREM_PROC_PT Gy_PROC; + /* emulated AAA-Server */ + var DIAMETER_Emulation_CT vc_S6b; + port DIAMETER_PT S6b_UNIT; + port DIAMETEREM_PROC_PT S6b_PROC; /* global test case guard timer (actual timeout value is set in f_init()) */ timer T_guard; } @@ -97,6 +105,7 @@ type component PGW_Session_CT extends GTP2_ConnHdlr { port DIAMETER_Conn_PT Gx; port DIAMETER_Conn_PT Gy; + port DIAMETER_Conn_PT S6b; /* GTP-U IPv4 address remote sie */ var OCT4 g_gtpu4_remote; @@ -128,7 +137,7 @@ type record SessionPars { hexstring imsi, hexstring msisdn optional, // serving network - integer rat_type, + GTP2C_RAT_Type rat_type, // flags? charstring apn, /* Apn subscribed or non-subscribed */ @@ -155,8 +164,13 @@ type record SessionPars { } template (value) SessionPars -t_SessionPars(hexstring imsi, charstring tundev, template (omit) hexstring msisdn := '1234'H, integer rat_type := 6, charstring apn := "internet", - boolean selection_mode := false, BIT3 pdn_type := '001'B) := { +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, + BIT3 pdn_type := '001'B) := { imsi := imsi, msisdn := msisdn, rat_type := rat_type, @@ -228,8 +242,26 @@ friend function f_init_diameter(charstring id) runs on PGW_Test_CT { connect(vc_Gy:DIAMETER_PROC, self:Gy_PROC); vc_Gy.start(DIAMETER_Emulation.main(ops, pars, id)); + /* S6b setup: */ + pars := { + remote_ip := mp_pgw_hostname, + remote_sctp_port := -1, + local_ip := mp_aaa_local_ip, + local_sctp_port := mp_aaa_local_port, + origin_host := "aaa." & mp_diam_realm, + origin_realm := mp_diam_realm, + auth_app_id := c_DIAMETER_3GPP_S6b_AID, + vendor_app_id := c_DIAMETER_3GPP_S6b_AID + }; + vc_S6b := DIAMETER_Emulation_CT.create(id); + map(vc_S6b:DIAMETER, system:DIAMETER_CODEC_PT); + connect(vc_S6b:DIAMETER_UNIT, self:S6b_UNIT); + connect(vc_S6b:DIAMETER_PROC, self:S6b_PROC); + vc_S6b.start(DIAMETER_Emulation.main(ops, pars, id)); + f_diameter_wait_capability(Gx_UNIT); f_diameter_wait_capability(Gy_UNIT); + f_diameter_wait_capability(S6b_UNIT); /* Give some time for our emulation to get out of SUSPECT list of SUT (3 watchdong ping-pongs): * RFC6733 sec 5.1 * RFC3539 sec 3.4.1 [5] @@ -266,7 +298,7 @@ private function f_init(float guard_timeout := 60.0) runs on PGW_Test_CT { function f_start_handler(void_fn fn, template (omit) SessionPars pars_tmpl := omit) runs on PGW_Test_CT return PGW_Session_CT { var charstring id := testcasename(); - var DIAMETER_ConnHdlr_CT vc_conn_gx, vc_conn_gy; + var DIAMETER_ConnHdlr_CT vc_conn_gx, vc_conn_gy, vc_conn_s6b; var PGW_Session_CT vc_conn; var SessionPars pars; @@ -296,6 +328,14 @@ runs on PGW_Test_CT return PGW_Session_CT { vc_conn_gy.start(f_diam_connhldr_ct_main(pars.imsi)); } + if (isbound(vc_S6b)) { + vc_conn_s6b := DIAMETER_ConnHdlr_CT.create(id); + connect(vc_conn_s6b:DIAMETER, vc_S6b:DIAMETER_CLIENT); + connect(vc_conn_s6b:DIAMETER_PROC, vc_S6b:DIAMETER_PROC); + connect(vc_conn:S6b, vc_conn_s6b:DIAMETER_CLIENT); + vc_conn_s6b.start(f_diam_connhldr_ct_main(pars.imsi)); + } + vc_conn.start(f_handler_init(fn, pars)); return vc_conn; } @@ -325,6 +365,28 @@ function f_gen_imsi(integer suffix) return hexstring { return f_concat_pad(15, '26242'H, suffix); } +/* S6b emulation (AAA-Server) */ +private altstep as_DIA_S6b_AAR() runs on PGW_Session_CT { + var PDU_DIAMETER rx_dia; + [] S6b.receive(tr_DIA_S6b_AAR()) -> value rx_dia { + var template (omit) AVP avp; + var octetstring sess_id; + + avp := f_DIAMETER_get_avp(rx_dia, c_AVP_Code_BASE_NONE_Session_Id); + sess_id := valueof(avp.avp_data.avp_BASE_NONE_Session_Id); + + S6b.send(ts_DIA_S6b_AAA(sess_id, "aaa." & mp_diam_realm, + mp_diam_realm, mp_diam_realm, + rx_dia.hop_by_hop_id, rx_dia.end_to_end_id)); + setverdict(pass); + } + [] 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; [] Gx.receive(tr_DIA_Gx_CCR(req_type := req_type)) -> value rx_dia { @@ -347,6 +409,7 @@ private altstep as_DIA_Gx_CCR(DCC_NONE_CC_Request_Type req_type) runs on PGW_Ses } } +/* 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 { @@ -359,7 +422,7 @@ runs on PGW_Session_CT return template (present) PDU_DIAMETER var template (present) OCT1 nsapi := ?; imsi := char2oct(f_dec_TBCD(imsi_hex2oct(g_pars.imsi))); //msisdn := char2oct(f_dec_TBCD(substr(ctx_val.msisdn, 1, lengthof(ctx_val.msisdn) -1))); - rat_type := int2oct(g_pars.rat_type, 1); + rat_type := int2oct(enum2int(g_pars.rat_type), 1); charging_char := char2oct(oct2str('0000'O)); // f_s5s8_create_session() uses hardcoded chg_car := '0000'O nsapi := int2oct(g_pars.bearer.ebi, 1); select (req_type) { @@ -538,6 +601,10 @@ runs on PGW_Session_CT { } } +/* 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, @@ -558,12 +625,23 @@ return template (omit) FullyQualifiedTEID /* process one to-be-created bearer context */ private function process_bctx_create(BearerContextGrouped bctx) runs on PGW_Session_CT { - /* FIXME: EPS Bearer ID */ + 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, 5, '0010'B)); + 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; @@ -601,10 +679,19 @@ private function f_create_session(template (value) FullyQualifiedTEID fteid_c_ie template (omit) UserLocationInfo uli_ie := omit) 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 := g_pars.rat_type, + rat_type := enum2int(g_pars.rat_type), sender_fteid := fteid_c_ie, apn := f_enc_dns_hostname(g_pars.apn), pdn_type := g_pars.pdn_type, @@ -615,14 +702,20 @@ private function f_create_session(template (value) FullyQualifiedTEID fteid_c_ie g2c.gtpcv2_pdu.createSessionRequest.servingNetwork := ts_GTP2C_ServingNetwork('001'H, '01F'H); GTP2.send(g2c); + if (do_s6b and S6b.checkstate("Connected")) { + as_DIA_S6b_AAR(); + } if (Gx.checkstate("Connected")) { as_DIA_Gx_CCR(INITIAL_REQUEST); } + /* FIXME: When on S2b interface, SMF is not using the Gy interface, unknown reason. */ if (Gy.checkstate("Connected")) { as_DIA_Gy_CCR(INITIAL_REQUEST); } alt { - [] GTP2.receive(tr_GTP2C_CreateSessionResp(d_teid:=g_pars.teic_local, cause:=Request_accepted)) -> value rx { + [] GTP2.receive(tr_GTP2C_CreateSessionResp(d_teid := g_pars.teic_local, + cause := Request_accepted, + apn_restriction := apn_restriction)) -> value rx { /* extract TEIDs */ var CreateSessionResponse resp := rx.gtpcv2_pdu.createSessionResponse; g_pars.teic_remote := resp.fullyQualifiedTEID[0].tEID_GRE_Key; @@ -686,6 +779,25 @@ private function f_s5s8_create_session() runs on PGW_Session_CT { } +/* create a session on the PGW on a S2b interface (from ePDG)*/ +private function f_s2b_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_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); + f_create_session(fteid_c_ie, fteid_u_ie); + /* 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); + +} + /* delete the session from the PGW */ private function f_delete_session(template (omit) GTP2C_Cause tx_cause := omit, template (present) OCT4 exp_teid, @@ -934,6 +1046,21 @@ testcase TC_gy_charging_cc_time() runs on PGW_Test_CT { vc_conn.done; } +/* create a session, expect it to succeed */ +private function f_TC_s2b_createSession_v4_noapco() runs on PGW_Session_CT { + f_s2b_create_session(); + setverdict(pass); +} +testcase TC_s2b_createSession_v4_noapco() runs on PGW_Test_CT { + var PGW_Session_CT vc_conn; + var SessionPars pars := valueof(t_SessionPars('001010123456789'H, + "tun22", + rat_type := GTP2C_RAT_WLAN)); + f_init(); + vc_conn := f_start_handler(refers(f_TC_s2b_createSession_v4_noapco), pars); + vc_conn.done; +} + control { execute( TC_tx_echo() ); execute( TC_createSession() ); @@ -942,6 +1069,7 @@ control { execute( TC_createSession_deleteSession() ); execute( TC_deleteSession_unknown() ); execute( TC_gy_charging_cc_time() ); + execute( TC_s2b_createSession_v4_noapco() ); } diff --git a/pgw/expected-results.xml b/pgw/expected-results.xml index 0643289c..0e252afb 100644 --- a/pgw/expected-results.xml +++ b/pgw/expected-results.xml @@ -7,4 +7,5 @@ <testcase classname='PGW_Tests' name='TC_createSession_deleteSession' time='MASKED'/> <testcase classname='PGW_Tests' name='TC_deleteSession_unknown' time='MASKED'/> <testcase classname='PGW_Tests' name='TC_gy_charging_cc_time' time='MASKED'/> + <testcase classname='PGW_Tests' name='TC_s2b_createSession_v4_noapco' time='MASKED'/> </testsuite> diff --git a/pgw/gen_links.sh b/pgw/gen_links.sh index 9aebfc2c..40671f93 100755 --- a/pgw/gen_links.sh +++ b/pgw/gen_links.sh @@ -64,7 +64,7 @@ FILES+="GTPv2_PrivateExtensions.ttcn GTPv2_Templates.ttcn " FILES+="GTPv2_CodecPort.ttcn GTPv2_CodecPort_CtrlFunctDef.cc GTPv2_CodecPort_CtrlFunct.ttcn GTPv2_Emulation.ttcn " FILES+="DNS_Helpers.ttcn " FILES+="DIAMETER_Types.ttcn DIAMETER_CodecPort.ttcn DIAMETER_CodecPort_CtrlFunct.ttcn DIAMETER_CodecPort_CtrlFunctDef.cc DIAMETER_Emulation.ttcn " -FILES+="DIAMETER_Templates.ttcn DIAMETER_ts29_212_Templates.ttcn DIAMETER_ts29_272_Templates.ttcn DIAMETER_ts32_299_Templates.ttcn " +FILES+="DIAMETER_Templates.ttcn DIAMETER_ts29_212_Templates.ttcn DIAMETER_ts29_272_Templates.ttcn DIAMETER_rfc5447_Templates.ttcn DIAMETER_ts29_273_Templates.ttcn DIAMETER_ts32_299_Templates.ttcn " gen_links $DIR $FILES ignore_pp_results |