Loading .vscode/settings.json 0 → 100644 +3 −0 Changes for .vscode/settings.json: 3 added lines, 0 removed lines. Original line number Diff line number Diff line { "cmake.ignoreCMakeListsMissing": true } No newline at end of file ttcn/AtsNGAP/NGAP_Steps.ttcn +124 −7 Changes for ttcn/AtsNGAP/NGAP_Steps.ttcn: 124 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG import from LibGtpu_TypesAndValues all; import from LibGtpu_Templates all; import from LibGtpu_Functions all; import from LibNGAP_EncdecDeclarations all; import from UpperTesterPtcToMtcDefs all; import from UpperTesterFunctions all; Loading Loading @@ -865,12 +866,16 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG * session (PX_TRANSPORT_LAYER_ADDRESS, PX_GTP_TEID). With PICS_USE_GTPU, the test system plays that * UPF: each G-PDU received on N3_P meanwhile is checked (f_NGAP_gnb_check_uplink_gpdu), and the * verdict is set to fail if one does not conform, or if none carries an echo request of the UE * although the modem ran the pings. Echo requests are not answered (no downlink user data yet). * although the modem ran the pings. * With PX_GTPU_ANSWER_ECHO_REQUESTS, once the downlink tunnel endpoint of the gNB is known * (f_NGAP_gnb_get_dl_gtp_tunnel), each echo request is answered at once with an echo reply in a * downlink G-PDU (PDU Session Container DL PDU SESSION INFORMATION, QFI PX_QOS_FLOW_IDENTIFIER). The * modem must then report replies received: the verdict is set to fail if none of them reached it. * The UE stays in CM-CONNECTED meanwhile: no NGAP signalling is expected. If the gNB nevertheless * asks for the release of the UE context (UE CONTEXT RELEASE REQUEST, e.g. user inactivity when the * UE sends nothing), it is released (UE CONTEXT RELEASE COMMAND/COMPLETE) and the wait goes on: * left unanswered, the request would end in an NG RESET from the gNB. * @note Lost replies are not a failure. The UT dispatcher (NGAP_UtDispatcher) sets the verdict to inconc * @note Without downlink replies, lost replies are not a failure. The UT dispatcher (NGAP_UtDispatcher) sets the verdict to inconc * if the modem could not run the pings at all (e.g. the context of the data call is not usable by * AT+QPING). * @return true if the modem ran the pings and, with PICS_USE_GTPU, the uplink user data was conformant. Loading @@ -879,6 +884,8 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG var UT_COMMON_CNF v_cnf; var GtpuMessage v_gtpu; var integer v_echoRequests := 0; // Conformant G-PDUs carrying an echo request of the UE var integer v_echoReplies := 0; // Echo replies sent in downlink G-PDUs var integer v_received; // Echo replies the modem reports as received var boolean v_conformant := true; timer t_ping := int2float(PX_UT_PING_COUNT * PX_UT_PING_TIMEOUT) + 30.0; Loading Loading @@ -909,7 +916,7 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG repeat; } [PICS_USE_GTPU] N3_P.receive(mw_gtpu_any) -> value v_gtpu { fl_count_uplink_gpdu(v_gtpu, v_echoRequests, v_conformant); fl_count_uplink_gpdu(v_gtpu, v_echoRequests, v_echoReplies, v_conformant); repeat; } [] t_ping.timeout { Loading @@ -925,7 +932,7 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG t_ping.start(1.0); alt { [] N3_P.receive(mw_gtpu_any) -> value v_gtpu { fl_count_uplink_gpdu(v_gtpu, v_echoRequests, v_conformant); fl_count_uplink_gpdu(v_gtpu, v_echoRequests, v_echoReplies, v_conformant); repeat; } [] t_ping.timeout { Loading @@ -940,27 +947,137 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG if (v_conformant and (v_echoRequests > 0)) { log("*** " & __SCOPE__ & ": PASS: uplink user data forwarded by the gNB on N3 ***"); } if (v_echoReplies > 0) { v_received := fl_ping_replies_received(v_cnf); log("*** " & __SCOPE__ & ": INFO: ", v_echoReplies, " echo reply(ies) sent on N3, ", v_received, " received by the UE ***"); if (v_received == 0) { log("*** " & __SCOPE__ & ": FAIL: the UE received none of the echo replies sent to the gNB on N3 ***"); setverdict(fail, "no downlink user data delivered by the gNB to the UE"); return false; } log("*** " & __SCOPE__ & ": PASS: downlink user data delivered by the gNB to the UE ***"); } return v_cnf.Result and v_conformant and (v_echoRequests > 0); } // End of function f_NGAP_gnb_UE_ping /** * @brief Check an uplink G-PDU (f_NGAP_gnb_check_uplink_gpdu) and update the counters of f_NGAP_gnb_UE_ping. * @brief Check an uplink G-PDU (f_NGAP_gnb_check_uplink_gpdu), answer it if it is an echo request of the UE, * and update the counters of f_NGAP_gnb_UE_ping. * @details The echo reply (f_ipv4_icmpEchoReply) is sent in a downlink G-PDU to the downlink tunnel endpoint * of the gNB (vc_dlGtpTunnel), with PX_GTPU_ANSWER_ECHO_REQUESTS and once that endpoint is known. * @param p_gtpu The received GTP-U message. * @param p_echoRequests Incremented if it carries an echo request of the UE. * @param p_echoReplies Incremented if an echo reply was sent. * @param p_conformant Set to false if its GTP-U encapsulation does not conform. */ function fl_count_uplink_gpdu(in GtpuMessage p_gtpu, inout integer p_echoRequests, inout boolean p_conformant) runs on gNBNGAPComponent { function fl_count_uplink_gpdu( in GtpuMessage p_gtpu, inout integer p_echoRequests, inout integer p_echoReplies, inout boolean p_conformant ) runs on gNBNGAPComponent { log(">>> fl_count_uplink_gpdu: p_gtpu: ", p_gtpu); log(">>> fl_count_uplink_gpdu: p_echoRequests: ", p_echoRequests); var boolean v_echoRequest; var octetstring v_reply; if (not f_NGAP_gnb_check_uplink_gpdu(p_gtpu, v_echoRequest)) { p_conformant := false; } else if (v_echoRequest) { return; } if (not v_echoRequest) { return; } p_echoRequests := p_echoRequests + 1; if (not PX_GTPU_ANSWER_ECHO_REQUESTS or not vc_dlGtpTunnelKnown) { return; } v_reply := f_ipv4_icmpEchoReply(p_gtpu.payload); if (v_reply == ''O) { return; } N3_P.send(m_gtpu_gPdu_dl(vc_dlGtpTunnel.gTP_TEID, PX_QOS_FLOW_IDENTIFIER, v_reply)); p_echoReplies := p_echoReplies + 1; } // End of function fl_count_uplink_gpdu /** * @brief Number of echo replies the modem reports as received, from the result of the PING request. * @details The upper tester ends its result with the summary of the modem, "... rcvd=<n> ...", when the modem * gives one; otherwise each reply received is reported as a line "+QPING: 0,"<address>",...". * @param p_cnf The confirmation of the PING request. * @return The number of replies received. */ function fl_ping_replies_received(in UT_COMMON_CNF p_cnf) return integer { var charstring v_text := ""; var integer v_count := 0; if (ispresent(p_cnf.ResultString)) { v_text := p_cnf.ResultString; } for (var integer i := 0; i + 5 <= lengthof(v_text); i := i + 1) { if (substr(v_text, i, 5) == "rcvd=") { var charstring v_digits := ""; for (var integer j := i + 5; j < lengthof(v_text) and char2int(v_text[j]) >= char2int("0") and char2int(v_text[j]) <= char2int("9"); j := j + 1) { v_digits := v_digits & v_text[j]; } if (v_digits != "") { return str2int(v_digits); } } } for (var integer i := 0; i + 10 <= lengthof(v_text); i := i + 1) { if (substr(v_text, i, 10) == "+QPING: 0,") { v_count := v_count + 1; } } return v_count; } // End of function fl_ping_replies_received /** * @brief Get the downlink tunnel endpoint of the gNB for a PDU session from the PDU SESSION RESOURCE SETUP * RESPONSE just received (vc_recvNGAP_PDU), and keep it in vc_dlGtpTunnel. * @details The PDU Session Resource Setup Response Transfer of the item for p_pduSessionId is decoded (aligned * PER, TS 38.413 clause 9.3.4.2): its DL QoS Flow per TNL Information gives the GTP tunnel. * @param p_pduSessionId The PDU session. * @return false if the response has no such item, or the tunnel cannot be decoded. */ function f_NGAP_gnb_get_dl_gtp_tunnel(in PDUSessionID p_pduSessionId) runs on gNBNGAPComponent return boolean { var PDUSessionResourceSetupResponseTransfer v_transfer; var bitstring v_bs; vc_dlGtpTunnelKnown := false; if (not ischosen(vc_recvNGAP_PDU.successfulOutcome)) { return false; } for (var integer i := 0; i < lengthof(vc_recvNGAP_PDU.successfulOutcome.value_.pDUSessionResourceSetupResponse.protocolIEs); i := i + 1) { if (vc_recvNGAP_PDU.successfulOutcome.value_.pDUSessionResourceSetupResponse.protocolIEs[i].id == id_PDUSessionResourceSetupListSURes) { var PDUSessionResourceSetupListSURes v_list := vc_recvNGAP_PDU.successfulOutcome.value_.pDUSessionResourceSetupResponse.protocolIEs[i].value_.pDUSessionResourceSetupListSURes; for (var integer j := 0; j < lengthof(v_list); j := j + 1) { if (v_list[j].pDUSessionID == p_pduSessionId) { v_bs := oct2bit(v_list[j].pDUSessionResourceSetupResponseTransfer); if (fx_dec_PDUSessionResourceSetupResponseTransfer(v_bs, v_transfer) != 0) { log("f_NGAP_gnb_get_dl_gtp_tunnel: decoding failure: ", v_list[j].pDUSessionResourceSetupResponseTransfer); return false; } if (not ischosen(v_transfer.dLQosFlowPerTNLInformation.uPTransportLayerInformation.gTPTunnel)) { log("f_NGAP_gnb_get_dl_gtp_tunnel: no GTP tunnel: ", v_transfer); return false; } vc_dlGtpTunnel := v_transfer.dLQosFlowPerTNLInformation.uPTransportLayerInformation.gTPTunnel; vc_dlGtpTunnelKnown := true; log("f_NGAP_gnb_get_dl_gtp_tunnel: downlink tunnel of the gNB: ", f_ipv4_toString(bit2oct(vc_dlGtpTunnel.transportLayerAddress)), ", TEID ", vc_dlGtpTunnel.gTP_TEID, " (G-PDUs are sent to the dst_ip of system.N3_gNB_1)"); return true; } } } } log("f_NGAP_gnb_get_dl_gtp_tunnel: no setup item for PDU session ", p_pduSessionId); return false; } // End of function f_NGAP_gnb_get_dl_gtp_tunnel /** * @brief Check an uplink G-PDU the gNB forwarded on N3 for the PDU session of the UE (TS 29.281, TS 38.415). * @details The gNB is responsible for the GTP-U encapsulation, which must conform: a G-PDU, to the uplink Loading ttcn/AtsNGAP/NGAP_TCFunctions.ttcn +3 −0 Changes for ttcn/AtsNGAP/NGAP_TCFunctions.ttcn: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -176,6 +176,9 @@ module NGAP_TCFunctions { // With a real UE, the PDU session must also be usable from the UE: the upper tester reports // the data call result once the modem has its IP address (inconc otherwise, see // f_NGAP_gnb_UE_await_data_call) if (PICS_USE_GTPU and v_setup and not f_NGAP_gnb_get_dl_gtp_tunnel(v_pduSessionId)) { log("*** " & __SCOPE__ & ": INFO: downlink tunnel of the gNB unknown, no downlink user data ***"); } if (PICS_USE_AT_COMMANDS and v_setup) { if (not f_NGAP_gnb_UE_await_data_call()) { log("*** " & __SCOPE__ & ": INCONC: the PDU session is set up, but the UE data call is not connected. ***"); Loading ttcn/LibGtpu/lib/LibGtpu_Functions.ttcn +54 −0 Changes for ttcn/LibGtpu/lib/LibGtpu_Functions.ttcn: 54 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -125,4 +125,58 @@ module LibGtpu_Functions { return v_result; } // End of function f_ipv4_toString /** * @brief Internet checksum (IETF RFC 1071): one's complement of the one's complement sum of 16-bit words. * @param p_data The data, with its checksum field set to zero; padded with a zero octet if its length is odd. * @return The two octets of the checksum. */ function f_inet_checksum(in octetstring p_data) return octetstring { var octetstring v_data := p_data; var integer v_sum := 0; if (lengthof(v_data) mod 2 == 1) { v_data := v_data & '00'O; } for (var integer i := 0; i < lengthof(v_data); i := i + 2) { v_sum := v_sum + oct2int(substr(v_data, i, 2)); } while (v_sum > 65535) { v_sum := (v_sum mod 65536) + (v_sum / 65536); } return int2oct(65535 - v_sum, 2); } // End of function f_inet_checksum /** * @brief Build the ICMP echo reply to an IPv4 ICMP echo request (IETF RFC 792), as its destination would. * @details The IPv4 header is copied (options included) with the source and destination addresses swapped, the * TTL set to p_ttl and the header checksum recomputed. The ICMP message keeps the identifier, the sequence * number and the data of the request, with the type 0 (echo reply) and the checksum recomputed. * @param p_request The echo request, a complete IPv4 packet (see f_ipv4_getHeader). * @param p_ttl The TTL of the reply. * @return The echo reply, or ''O if p_request is not an IPv4 ICMP echo request. */ function f_ipv4_icmpEchoReply(in octetstring p_request, in integer p_ttl := 64) return octetstring { var octetstring v_source; var octetstring v_destination; var integer v_protocol; var octetstring v_icmp; var octetstring v_header; var integer v_ihl; if (not f_ipv4_getHeader(p_request, v_source, v_destination, v_protocol, v_icmp) or (v_protocol != 1) or (lengthof(v_icmp) < 8) or (v_icmp[0] != '08'O)) { return ''O; } v_ihl := (oct2int(p_request[0]) mod 16) * 4; // Version/IHL, DSCP/ECN, total length, identification, flags/fragment offset | TTL | protocol | checksum 0 | // source := destination | destination := source | options v_header := substr(p_request, 0, 8) & int2oct(p_ttl, 1) & p_request[9] & '0000'O & v_destination & v_source & substr(p_request, 20, v_ihl - 20); v_header := substr(v_header, 0, 10) & f_inet_checksum(v_header) & substr(v_header, 12, v_ihl - 12); // Type 0 (echo reply) | code | checksum 0 | identifier, sequence number and data of the request v_icmp := '00'O & v_icmp[1] & '0000'O & substr(v_icmp, 4, lengthof(v_icmp) - 4); v_icmp := substr(v_icmp, 0, 2) & f_inet_checksum(v_icmp) & substr(v_icmp, 4, lengthof(v_icmp) - 4); return v_header & v_icmp; } // End of function f_ipv4_icmpEchoReply } // End of module LibGtpu_Functions ttcn/LibNGAP/lib/LibNGAP_Pixits.ttcn +4 −0 Changes for ttcn/LibNGAP/lib/LibNGAP_Pixits.ttcn: 4 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -760,6 +760,10 @@ module LibNGAP_Pixits { /** @desc Time, in seconds, the real UE waits for each echo reply (1..255); see f_NGAP_gnb_UE_ping */ modulepar integer PX_UT_PING_TIMEOUT := 4; /** @desc Whether the test system, playing the UPF (PICS_USE_GTPU), answers the echo requests of the ping of the real UE * with echo replies sent in downlink G-PDUs to the gNB; see f_NGAP_gnb_UE_ping */ modulepar boolean PX_GTPU_ANSWER_ECHO_REQUESTS := true; /** @desc Whether, after its restart (AT+CFUN=1,1), the real UE (AT commands) is set to full functionality (AT+CFUN=1) and * forced onto PX_UT_PLMN_MCC/PX_UT_PLMN_MNC (AT+COPS=1); see f_NGAP_gnb_UE_Register. * false: it is only restarted, and registers by itself on the PLMN it selects. */ Loading Loading
.vscode/settings.json 0 → 100644 +3 −0 Changes for .vscode/settings.json: 3 added lines, 0 removed lines. Original line number Diff line number Diff line { "cmake.ignoreCMakeListsMissing": true } No newline at end of file
ttcn/AtsNGAP/NGAP_Steps.ttcn +124 −7 Changes for ttcn/AtsNGAP/NGAP_Steps.ttcn: 124 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG import from LibGtpu_TypesAndValues all; import from LibGtpu_Templates all; import from LibGtpu_Functions all; import from LibNGAP_EncdecDeclarations all; import from UpperTesterPtcToMtcDefs all; import from UpperTesterFunctions all; Loading Loading @@ -865,12 +866,16 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG * session (PX_TRANSPORT_LAYER_ADDRESS, PX_GTP_TEID). With PICS_USE_GTPU, the test system plays that * UPF: each G-PDU received on N3_P meanwhile is checked (f_NGAP_gnb_check_uplink_gpdu), and the * verdict is set to fail if one does not conform, or if none carries an echo request of the UE * although the modem ran the pings. Echo requests are not answered (no downlink user data yet). * although the modem ran the pings. * With PX_GTPU_ANSWER_ECHO_REQUESTS, once the downlink tunnel endpoint of the gNB is known * (f_NGAP_gnb_get_dl_gtp_tunnel), each echo request is answered at once with an echo reply in a * downlink G-PDU (PDU Session Container DL PDU SESSION INFORMATION, QFI PX_QOS_FLOW_IDENTIFIER). The * modem must then report replies received: the verdict is set to fail if none of them reached it. * The UE stays in CM-CONNECTED meanwhile: no NGAP signalling is expected. If the gNB nevertheless * asks for the release of the UE context (UE CONTEXT RELEASE REQUEST, e.g. user inactivity when the * UE sends nothing), it is released (UE CONTEXT RELEASE COMMAND/COMPLETE) and the wait goes on: * left unanswered, the request would end in an NG RESET from the gNB. * @note Lost replies are not a failure. The UT dispatcher (NGAP_UtDispatcher) sets the verdict to inconc * @note Without downlink replies, lost replies are not a failure. The UT dispatcher (NGAP_UtDispatcher) sets the verdict to inconc * if the modem could not run the pings at all (e.g. the context of the data call is not usable by * AT+QPING). * @return true if the modem ran the pings and, with PICS_USE_GTPU, the uplink user data was conformant. Loading @@ -879,6 +884,8 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG var UT_COMMON_CNF v_cnf; var GtpuMessage v_gtpu; var integer v_echoRequests := 0; // Conformant G-PDUs carrying an echo request of the UE var integer v_echoReplies := 0; // Echo replies sent in downlink G-PDUs var integer v_received; // Echo replies the modem reports as received var boolean v_conformant := true; timer t_ping := int2float(PX_UT_PING_COUNT * PX_UT_PING_TIMEOUT) + 30.0; Loading Loading @@ -909,7 +916,7 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG repeat; } [PICS_USE_GTPU] N3_P.receive(mw_gtpu_any) -> value v_gtpu { fl_count_uplink_gpdu(v_gtpu, v_echoRequests, v_conformant); fl_count_uplink_gpdu(v_gtpu, v_echoRequests, v_echoReplies, v_conformant); repeat; } [] t_ping.timeout { Loading @@ -925,7 +932,7 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG t_ping.start(1.0); alt { [] N3_P.receive(mw_gtpu_any) -> value v_gtpu { fl_count_uplink_gpdu(v_gtpu, v_echoRequests, v_conformant); fl_count_uplink_gpdu(v_gtpu, v_echoRequests, v_echoReplies, v_conformant); repeat; } [] t_ping.timeout { Loading @@ -940,27 +947,137 @@ module NGAP_Steps { // TODO FSCOM f_xxx_UE_SecurityMode to be mmoved into NAG_NG if (v_conformant and (v_echoRequests > 0)) { log("*** " & __SCOPE__ & ": PASS: uplink user data forwarded by the gNB on N3 ***"); } if (v_echoReplies > 0) { v_received := fl_ping_replies_received(v_cnf); log("*** " & __SCOPE__ & ": INFO: ", v_echoReplies, " echo reply(ies) sent on N3, ", v_received, " received by the UE ***"); if (v_received == 0) { log("*** " & __SCOPE__ & ": FAIL: the UE received none of the echo replies sent to the gNB on N3 ***"); setverdict(fail, "no downlink user data delivered by the gNB to the UE"); return false; } log("*** " & __SCOPE__ & ": PASS: downlink user data delivered by the gNB to the UE ***"); } return v_cnf.Result and v_conformant and (v_echoRequests > 0); } // End of function f_NGAP_gnb_UE_ping /** * @brief Check an uplink G-PDU (f_NGAP_gnb_check_uplink_gpdu) and update the counters of f_NGAP_gnb_UE_ping. * @brief Check an uplink G-PDU (f_NGAP_gnb_check_uplink_gpdu), answer it if it is an echo request of the UE, * and update the counters of f_NGAP_gnb_UE_ping. * @details The echo reply (f_ipv4_icmpEchoReply) is sent in a downlink G-PDU to the downlink tunnel endpoint * of the gNB (vc_dlGtpTunnel), with PX_GTPU_ANSWER_ECHO_REQUESTS and once that endpoint is known. * @param p_gtpu The received GTP-U message. * @param p_echoRequests Incremented if it carries an echo request of the UE. * @param p_echoReplies Incremented if an echo reply was sent. * @param p_conformant Set to false if its GTP-U encapsulation does not conform. */ function fl_count_uplink_gpdu(in GtpuMessage p_gtpu, inout integer p_echoRequests, inout boolean p_conformant) runs on gNBNGAPComponent { function fl_count_uplink_gpdu( in GtpuMessage p_gtpu, inout integer p_echoRequests, inout integer p_echoReplies, inout boolean p_conformant ) runs on gNBNGAPComponent { log(">>> fl_count_uplink_gpdu: p_gtpu: ", p_gtpu); log(">>> fl_count_uplink_gpdu: p_echoRequests: ", p_echoRequests); var boolean v_echoRequest; var octetstring v_reply; if (not f_NGAP_gnb_check_uplink_gpdu(p_gtpu, v_echoRequest)) { p_conformant := false; } else if (v_echoRequest) { return; } if (not v_echoRequest) { return; } p_echoRequests := p_echoRequests + 1; if (not PX_GTPU_ANSWER_ECHO_REQUESTS or not vc_dlGtpTunnelKnown) { return; } v_reply := f_ipv4_icmpEchoReply(p_gtpu.payload); if (v_reply == ''O) { return; } N3_P.send(m_gtpu_gPdu_dl(vc_dlGtpTunnel.gTP_TEID, PX_QOS_FLOW_IDENTIFIER, v_reply)); p_echoReplies := p_echoReplies + 1; } // End of function fl_count_uplink_gpdu /** * @brief Number of echo replies the modem reports as received, from the result of the PING request. * @details The upper tester ends its result with the summary of the modem, "... rcvd=<n> ...", when the modem * gives one; otherwise each reply received is reported as a line "+QPING: 0,"<address>",...". * @param p_cnf The confirmation of the PING request. * @return The number of replies received. */ function fl_ping_replies_received(in UT_COMMON_CNF p_cnf) return integer { var charstring v_text := ""; var integer v_count := 0; if (ispresent(p_cnf.ResultString)) { v_text := p_cnf.ResultString; } for (var integer i := 0; i + 5 <= lengthof(v_text); i := i + 1) { if (substr(v_text, i, 5) == "rcvd=") { var charstring v_digits := ""; for (var integer j := i + 5; j < lengthof(v_text) and char2int(v_text[j]) >= char2int("0") and char2int(v_text[j]) <= char2int("9"); j := j + 1) { v_digits := v_digits & v_text[j]; } if (v_digits != "") { return str2int(v_digits); } } } for (var integer i := 0; i + 10 <= lengthof(v_text); i := i + 1) { if (substr(v_text, i, 10) == "+QPING: 0,") { v_count := v_count + 1; } } return v_count; } // End of function fl_ping_replies_received /** * @brief Get the downlink tunnel endpoint of the gNB for a PDU session from the PDU SESSION RESOURCE SETUP * RESPONSE just received (vc_recvNGAP_PDU), and keep it in vc_dlGtpTunnel. * @details The PDU Session Resource Setup Response Transfer of the item for p_pduSessionId is decoded (aligned * PER, TS 38.413 clause 9.3.4.2): its DL QoS Flow per TNL Information gives the GTP tunnel. * @param p_pduSessionId The PDU session. * @return false if the response has no such item, or the tunnel cannot be decoded. */ function f_NGAP_gnb_get_dl_gtp_tunnel(in PDUSessionID p_pduSessionId) runs on gNBNGAPComponent return boolean { var PDUSessionResourceSetupResponseTransfer v_transfer; var bitstring v_bs; vc_dlGtpTunnelKnown := false; if (not ischosen(vc_recvNGAP_PDU.successfulOutcome)) { return false; } for (var integer i := 0; i < lengthof(vc_recvNGAP_PDU.successfulOutcome.value_.pDUSessionResourceSetupResponse.protocolIEs); i := i + 1) { if (vc_recvNGAP_PDU.successfulOutcome.value_.pDUSessionResourceSetupResponse.protocolIEs[i].id == id_PDUSessionResourceSetupListSURes) { var PDUSessionResourceSetupListSURes v_list := vc_recvNGAP_PDU.successfulOutcome.value_.pDUSessionResourceSetupResponse.protocolIEs[i].value_.pDUSessionResourceSetupListSURes; for (var integer j := 0; j < lengthof(v_list); j := j + 1) { if (v_list[j].pDUSessionID == p_pduSessionId) { v_bs := oct2bit(v_list[j].pDUSessionResourceSetupResponseTransfer); if (fx_dec_PDUSessionResourceSetupResponseTransfer(v_bs, v_transfer) != 0) { log("f_NGAP_gnb_get_dl_gtp_tunnel: decoding failure: ", v_list[j].pDUSessionResourceSetupResponseTransfer); return false; } if (not ischosen(v_transfer.dLQosFlowPerTNLInformation.uPTransportLayerInformation.gTPTunnel)) { log("f_NGAP_gnb_get_dl_gtp_tunnel: no GTP tunnel: ", v_transfer); return false; } vc_dlGtpTunnel := v_transfer.dLQosFlowPerTNLInformation.uPTransportLayerInformation.gTPTunnel; vc_dlGtpTunnelKnown := true; log("f_NGAP_gnb_get_dl_gtp_tunnel: downlink tunnel of the gNB: ", f_ipv4_toString(bit2oct(vc_dlGtpTunnel.transportLayerAddress)), ", TEID ", vc_dlGtpTunnel.gTP_TEID, " (G-PDUs are sent to the dst_ip of system.N3_gNB_1)"); return true; } } } } log("f_NGAP_gnb_get_dl_gtp_tunnel: no setup item for PDU session ", p_pduSessionId); return false; } // End of function f_NGAP_gnb_get_dl_gtp_tunnel /** * @brief Check an uplink G-PDU the gNB forwarded on N3 for the PDU session of the UE (TS 29.281, TS 38.415). * @details The gNB is responsible for the GTP-U encapsulation, which must conform: a G-PDU, to the uplink Loading
ttcn/AtsNGAP/NGAP_TCFunctions.ttcn +3 −0 Changes for ttcn/AtsNGAP/NGAP_TCFunctions.ttcn: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -176,6 +176,9 @@ module NGAP_TCFunctions { // With a real UE, the PDU session must also be usable from the UE: the upper tester reports // the data call result once the modem has its IP address (inconc otherwise, see // f_NGAP_gnb_UE_await_data_call) if (PICS_USE_GTPU and v_setup and not f_NGAP_gnb_get_dl_gtp_tunnel(v_pduSessionId)) { log("*** " & __SCOPE__ & ": INFO: downlink tunnel of the gNB unknown, no downlink user data ***"); } if (PICS_USE_AT_COMMANDS and v_setup) { if (not f_NGAP_gnb_UE_await_data_call()) { log("*** " & __SCOPE__ & ": INCONC: the PDU session is set up, but the UE data call is not connected. ***"); Loading
ttcn/LibGtpu/lib/LibGtpu_Functions.ttcn +54 −0 Changes for ttcn/LibGtpu/lib/LibGtpu_Functions.ttcn: 54 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -125,4 +125,58 @@ module LibGtpu_Functions { return v_result; } // End of function f_ipv4_toString /** * @brief Internet checksum (IETF RFC 1071): one's complement of the one's complement sum of 16-bit words. * @param p_data The data, with its checksum field set to zero; padded with a zero octet if its length is odd. * @return The two octets of the checksum. */ function f_inet_checksum(in octetstring p_data) return octetstring { var octetstring v_data := p_data; var integer v_sum := 0; if (lengthof(v_data) mod 2 == 1) { v_data := v_data & '00'O; } for (var integer i := 0; i < lengthof(v_data); i := i + 2) { v_sum := v_sum + oct2int(substr(v_data, i, 2)); } while (v_sum > 65535) { v_sum := (v_sum mod 65536) + (v_sum / 65536); } return int2oct(65535 - v_sum, 2); } // End of function f_inet_checksum /** * @brief Build the ICMP echo reply to an IPv4 ICMP echo request (IETF RFC 792), as its destination would. * @details The IPv4 header is copied (options included) with the source and destination addresses swapped, the * TTL set to p_ttl and the header checksum recomputed. The ICMP message keeps the identifier, the sequence * number and the data of the request, with the type 0 (echo reply) and the checksum recomputed. * @param p_request The echo request, a complete IPv4 packet (see f_ipv4_getHeader). * @param p_ttl The TTL of the reply. * @return The echo reply, or ''O if p_request is not an IPv4 ICMP echo request. */ function f_ipv4_icmpEchoReply(in octetstring p_request, in integer p_ttl := 64) return octetstring { var octetstring v_source; var octetstring v_destination; var integer v_protocol; var octetstring v_icmp; var octetstring v_header; var integer v_ihl; if (not f_ipv4_getHeader(p_request, v_source, v_destination, v_protocol, v_icmp) or (v_protocol != 1) or (lengthof(v_icmp) < 8) or (v_icmp[0] != '08'O)) { return ''O; } v_ihl := (oct2int(p_request[0]) mod 16) * 4; // Version/IHL, DSCP/ECN, total length, identification, flags/fragment offset | TTL | protocol | checksum 0 | // source := destination | destination := source | options v_header := substr(p_request, 0, 8) & int2oct(p_ttl, 1) & p_request[9] & '0000'O & v_destination & v_source & substr(p_request, 20, v_ihl - 20); v_header := substr(v_header, 0, 10) & f_inet_checksum(v_header) & substr(v_header, 12, v_ihl - 12); // Type 0 (echo reply) | code | checksum 0 | identifier, sequence number and data of the request v_icmp := '00'O & v_icmp[1] & '0000'O & substr(v_icmp, 4, lengthof(v_icmp) - 4); v_icmp := substr(v_icmp, 0, 2) & f_inet_checksum(v_icmp) & substr(v_icmp, 4, lengthof(v_icmp) - 4); return v_header & v_icmp; } // End of function f_ipv4_icmpEchoReply } // End of module LibGtpu_Functions
ttcn/LibNGAP/lib/LibNGAP_Pixits.ttcn +4 −0 Changes for ttcn/LibNGAP/lib/LibNGAP_Pixits.ttcn: 4 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -760,6 +760,10 @@ module LibNGAP_Pixits { /** @desc Time, in seconds, the real UE waits for each echo reply (1..255); see f_NGAP_gnb_UE_ping */ modulepar integer PX_UT_PING_TIMEOUT := 4; /** @desc Whether the test system, playing the UPF (PICS_USE_GTPU), answers the echo requests of the ping of the real UE * with echo replies sent in downlink G-PDUs to the gNB; see f_NGAP_gnb_UE_ping */ modulepar boolean PX_GTPU_ANSWER_ECHO_REQUESTS := true; /** @desc Whether, after its restart (AT+CFUN=1,1), the real UE (AT commands) is set to full functionality (AT+CFUN=1) and * forced onto PX_UT_PLMN_MCC/PX_UT_PLMN_MNC (AT+COPS=1); see f_NGAP_gnb_UE_Register. * false: it is only restarted, and registers by itself on the PLMN it selects. */ Loading