From 573effc0451b0fb5e3deeeeeaa49e88485c8417e Mon Sep 17 00:00:00 2001 From: seladb Date: Sat, 9 Nov 2024 00:00:00 -0800 Subject: [PATCH] Add GTPv2 layer (#1634) * Add GTPv2 layer * Add GTPv2 to READMEs * Fix clang-format * Few fixes * Another fix * Add doxygen * Fix doxygen * Address PR comments * Shorten version for initializing vector of tuples * Change `auto` -> `auto*` --- Packet++/header/GtpLayer.h | 823 +++++++++++++++++- Packet++/header/LdapLayer.h | 2 +- Packet++/header/ProtocolType.h | 9 +- Packet++/header/TLVData.h | 12 +- Packet++/src/GtpLayer.cpp | 718 ++++++++++++++- Packet++/src/TcpLayer.cpp | 4 + Packet++/src/UdpLayer.cpp | 3 + README.md | 2 +- .../PacketExamples/gtpv2-no-info-elements.dat | 1 + .../gtpv2-non-zero-cf-flag-instance.dat | 1 + .../PacketExamples/gtpv2-over-tcp.dat | 1 + .../gtpv2-with-piggyback-malformed.dat | 1 + .../PacketExamples/gtpv2-with-piggyback.dat | 1 + .../PacketExamples/gtpv2-with-teid.dat | 1 + Tests/Packet++Test/PacketExamples/gtpv2.pcap | Bin 0 -> 1184 bytes Tests/Packet++Test/TestDefinition.h | 9 +- Tests/Packet++Test/Tests/GtpTests.cpp | 359 +++++++- Tests/Packet++Test/main.cpp | 9 +- translation/README-kor.md | 2 +- translation/README-zh-tw.md | 2 +- 20 files changed, 1926 insertions(+), 34 deletions(-) create mode 100644 Tests/Packet++Test/PacketExamples/gtpv2-no-info-elements.dat create mode 100644 Tests/Packet++Test/PacketExamples/gtpv2-non-zero-cf-flag-instance.dat create mode 100644 Tests/Packet++Test/PacketExamples/gtpv2-over-tcp.dat create mode 100644 Tests/Packet++Test/PacketExamples/gtpv2-with-piggyback-malformed.dat create mode 100644 Tests/Packet++Test/PacketExamples/gtpv2-with-piggyback.dat create mode 100644 Tests/Packet++Test/PacketExamples/gtpv2-with-teid.dat create mode 100644 Tests/Packet++Test/PacketExamples/gtpv2.pcap diff --git a/Packet++/header/GtpLayer.h b/Packet++/header/GtpLayer.h index ffebf0451..74c9ba782 100644 --- a/Packet++/header/GtpLayer.h +++ b/Packet++/header/GtpLayer.h @@ -1,6 +1,9 @@ #pragma once #include "Layer.h" +#include "TLVData.h" +#include +#include /// @file @@ -373,7 +376,7 @@ namespace pcpp * @return True if the value was set successfully, false otherwise. In case of failure a corresponding error * message will be written to log */ - bool setSequenceNumber(const uint16_t seqNumber); + bool setSequenceNumber(uint16_t seqNumber); /** * Get the N-PDU number if exists on the message (N-PDU number is an optional field in GTP messages) @@ -389,7 +392,7 @@ namespace pcpp * @return True if the value was set successfully, false otherwise. In case of failure a corresponding error * message will be written to log */ - bool setNpduNumber(const uint8_t npduNum); + bool setNpduNumber(uint8_t npduNum); /** * Get the type of the next header extension if exists on the message (extensions are optional in GTP messages) @@ -441,7 +444,7 @@ namespace pcpp /** * A static method that checks whether the port is considered as GTPv1 * @param[in] port The port number to be checked - * @return True if the port matches those associated with the BGP protocol + * @return True if the port matches those associated with the GTPv1 protocol */ static bool isGTPv1Port(uint16_t port) { @@ -476,4 +479,818 @@ namespace pcpp return OsiModelTransportLayer; } }; + + /** + * @class GtpV2MessageType + * The enum wrapper class of GTPv2 message type + */ + class GtpV2MessageType + { + public: + /** + * Define enum types and the corresponding int values + */ + enum Value : uint8_t + { + /** Unknown message */ + Unknown = 0, + /** Echo Request message */ + EchoRequest = 1, + /** Echo Response message */ + EchoResponse = 2, + /** Version Not Supported message */ + VersionNotSupported = 3, + /** Create Session Request message */ + CreateSessionRequest = 32, + /** Create Session Response message */ + CreateSessionResponse = 33, + /** Modify Bearer Request message */ + ModifyBearerRequest = 34, + /** Modify Bearer Response message */ + ModifyBearerResponse = 35, + /** Delete Session Request message */ + DeleteSessionRequest = 36, + /** Delete Session Response message */ + DeleteSessionResponse = 37, + /** Change Notification Request message */ + ChangeNotificationRequest = 38, + /** Change Notification Response message */ + ChangeNotificationResponse = 39, + /** Remote UE Report Notifications message */ + RemoteUEReportNotifications = 40, + /** Remote UE Report Acknowledge message */ + RemoteUEReportAcknowledge = 41, + /** Modify Bearer Command message */ + ModifyBearerCommand = 64, + /** Modify Bearer Failure message */ + ModifyBearerFailure = 65, + /** Delete Bearer Command message */ + DeleteBearerCommand = 66, + /** Delete Bearer Failure message */ + DeleteBearerFailure = 67, + /** Bearer Resource Command message */ + BearerResourceCommand = 68, + /** Bearer Resource Failure message */ + BearerResourceFailure = 69, + /** Downlink Data Notification Failure message */ + DownlinkDataNotificationFailure = 70, + /** Trace Session Activation message */ + TraceSessionActivation = 71, + /** Trace Session Deactivation message */ + TraceSessionDeactivation = 72, + /** Stop Paging Indication message */ + StopPagingIndication = 73, + /** Create Bearer Request message */ + CreateBearerRequest = 95, + /** Create Bearer Response message */ + CreateBearerResponse = 96, + /** Update Bearer Request message */ + UpdateBearerRequest = 97, + /** Update Bearer Response message */ + UpdateBearerResponse = 98, + /** Delete Bearer Request message */ + DeleteBearerRequest = 99, + /** Delete Bearer Response message */ + DeleteBearerResponse = 100, + /** Delete PDN Request message */ + DeletePDNRequest = 101, + /** Delete PDN Response message */ + DeletePDNResponse = 102, + /** PGW Downlink Notification message */ + PGWDownlinkNotification = 103, + /** PGW Downlink Acknowledge message */ + PGWDownlinkAcknowledge = 104, + /** Identification Request message */ + IdentificationRequest = 128, + /** Identification Response message */ + IdentificationResponse = 129, + /** Context Request message */ + ContextRequest = 130, + /** Context Response message */ + ContextResponse = 131, + /** Context Acknowledge message */ + ContextAcknowledge = 132, + /** Forward Relocation Request message */ + ForwardRelocationRequest = 133, + /** Forward Relocation Response message */ + ForwardRelocationResponse = 134, + /** Forward Relocation Notification message */ + ForwardRelocationNotification = 135, + /** Forward Relocation Acknowledge message */ + ForwardRelocationAcknowledge = 136, + /** Forward Access Notification message */ + ForwardAccessNotification = 137, + /** Forward Access Acknowledge message */ + ForwardAccessAcknowledge = 138, + /** Relocation Cancel Request message */ + RelocationCancelRequest = 139, + /** Relocation Cancel Response message */ + RelocationCancelResponse = 140, + /** Configuration Transfer Tunnel message */ + ConfigurationTransferTunnel = 141, + /** Detach Notification message */ + DetachNotification = 149, + /** Detach Acknowledge message */ + DetachAcknowledge = 150, + /** CS Paging message */ + CSPaging = 151, + /** RAN Information Relay message */ + RANInformationRelay = 152, + /** Alert MME Notification message */ + AlertMMENotification = 153, + /** Alert MME Acknowledge message */ + AlertMMEAcknowledge = 154, + /** UE Activity Notification message */ + UEActivityNotification = 155, + /** UE Activity Acknowledge message */ + UEActivityAcknowledge = 156, + /** ISR Status message */ + ISRStatus = 157, + /** Create Forwarding Request message */ + CreateForwardingRequest = 160, + /** Create Forwarding Response message */ + CreateForwardingResponse = 161, + /** Suspend Notification message */ + SuspendNotification = 162, + /** Suspend Acknowledge message */ + SuspendAcknowledge = 163, + /** Resume Notification message */ + ResumeNotification = 164, + /** Resume Acknowledge message */ + ResumeAcknowledge = 165, + /** Create Indirect Data Tunnel Request message */ + CreateIndirectDataTunnelRequest = 166, + /** Create Indirect Data Tunnel Response message */ + CreateIndirectDataTunnelResponse = 167, + /** Delete Indirect Data Tunnel Request message */ + DeleteIndirectDataTunnelRequest = 168, + /** Delete Indirect Data Tunnel Response message */ + DeleteIndirectDataTunnelResponse = 169, + /** Release Access Bearers Request message */ + ReleaseAccessBearersRequest = 170, + /** Release Access Bearers Response message */ + ReleaseAccessBearersResponse = 171, + /** Downlink Data Notification message */ + DownlinkDataNotification = 176, + /** Downlink Data Acknowledge message */ + DownlinkDataAcknowledge = 177, + /** PGW Restart Notification message */ + PGWRestartNotification = 179, + /** PGW Restart Acknowledge message */ + PGWRestartAcknowledge = 180, + /** Update PDN Connection Request message */ + UpdatePDNConnectionRequest = 200, + /** Update PDN Connection Response message */ + UpdatePDNConnectionResponse = 201, + /** Modify Access Bearers Request message */ + ModifyAccessBearersRequest = 211, + /** Modify Access Bearers Response message */ + ModifyAccessBearersResponse = 212, + /** MMBS Session Start Request message */ + MMBSSessionStartRequest = 231, + /** MMBS Session Start Response message */ + MMBSSessionStartResponse = 232, + /** MMBS Session Update Request message */ + MMBSSessionUpdateRequest = 233, + /** MMBS Session Update Response message */ + MMBSSessionUpdateResponse = 234, + /** MMBS Session Stop Request message */ + MMBSSessionStopRequest = 235, + /** MMBS Session Stop Response message */ + MMBSSessionStopResponse = 236 + }; + + GtpV2MessageType() = default; + + // cppcheck-suppress noExplicitConstructor + /** + * Construct GtpV2MessageType from Value enum + * @param[in] value the message type enum value + */ + constexpr GtpV2MessageType(Value value) : m_Value(value) + {} + + /** + * @return A string representation of the message type + */ + std::string toString() const; + + /** + * A static method that creates GtpV2MessageType from an integer value + * @param[in] value The message type integer value + * @return The message type that corresponds to the integer value. If the integer value + * doesn't corresponds to any message type, GtpV2MessageType::Unknown is returned + */ + static GtpV2MessageType fromUintValue(uint8_t value); + + // Allow switch and comparisons. + constexpr operator Value() const + { + return m_Value; + } + + // Prevent usage: if(GtpV2MessageType) + explicit operator bool() const = delete; + + private: + Value m_Value = GtpV2MessageType::Unknown; + }; + + /** + * @class GtpV2InformationElement + * A wrapper class for GTPv2 information elements (IE). This class does not create or modify IEs, but rather + * serves as a wrapper and provides useful methods for retrieving data from them + */ + class GtpV2InformationElement : public TLVRecord + { + public: + /** + * GTPv2 Information Element (IE) types as defined in 3GPP TS 29.274 + */ + enum class Type : uint8_t + { + /** Unknown or reserved value */ + Unknown = 0, + /** International Mobile Subscriber Identity */ + Imsi = 1, + /** Indicates the result of a procedure */ + Cause = 2, + /** Recovery counter for GTP path management */ + Recovery = 3, + /** Session Transfer Number for SRVCC */ + StnSr = 51, + /** Access Point Name */ + Apn = 71, + /** Aggregate Maximum Bit Rate */ + Ambr = 72, + /** EPS Bearer ID */ + Ebi = 73, + /** IPv4/IPv6 Address */ + IpAddress = 74, + /** Mobile Equipment Identity (IMEI or IMEISV) */ + Mei = 75, + /** Mobile Station International Subscriber Directory Number */ + Msisdn = 76, + /** Indication flags for various features and capabilities */ + Indication = 77, + /** Protocol Configuration Options */ + Pco = 78, + /** PDN Address Allocation */ + Paa = 79, + /** Bearer Level Quality of Service */ + BearerQos = 80, + /** Flow Level Quality of Service */ + FlowQos = 81, + /** Radio Access Technology Type */ + RatType = 82, + /** Current PLMN and MME identifier */ + ServingNetwork = 83, + /** Bearer Traffic Flow Template */ + BearerTft = 84, + /** Traffic Aggregation Description */ + Tad = 85, + /** User Location Information */ + Uli = 86, + /** Fully Qualified TEID */ + FTeid = 87, + /** Temporary Mobile Subscriber Identity */ + Tmsi = 88, + /** Global Core Network ID */ + GlobalCnId = 89, + /** S103 PDN Data Forwarding Info */ + S103PdnDataForwardingInfo = 90, + /** S1-U Data Forwarding Info */ + S1UDataForwardingInfo = 91, + /** Delay Value in integer multiples of 50 milliseconds */ + DelayValue = 92, + /** Bearer Context */ + BearerContext = 93, + /** Charging ID for this PDP context */ + ChargingId = 94, + /** Charging Characteristics */ + ChargingCharacteristics = 95, + /** Trace Information */ + TraceInformation = 96, + /** Bearer Flags */ + BearerFlags = 97, + /** PDN Type (IPv4, IPv6, IPv4v6) */ + PdnType = 99, + /** Procedure Transaction ID */ + Pti = 100, + /** MM Context (GSM Key and Triplets) */ + MmContext1 = 103, + /** MM Context (UMTS Key, Used Cipher and Quintuplets) */ + MmContext2 = 104, + /** MM Context (GSM Key, Used Cipher and Quintuplets) */ + MmContext3 = 105, + /** MM Context (UMTS Key and Quintuplets) */ + MmContext4 = 106, + /** MM Context (EPS Security Context, Quadruplets and Quintuplets) */ + MmContext5 = 107, + /** MM Context (UMTS Key, Quadruplets and Quintuplets) */ + MmContext6 = 108, + /** PDN Connection */ + PdnConnection = 109, + /** PDU Numbers */ + PduNumbers = 110, + /** Packet TMSI */ + PTmsi = 111, + /** P-TMSI Signature */ + PTmsiSignature = 112, + /** Hop Counter */ + HopCounter = 113, + /** UE Time Zone */ + UeTimeZone = 114, + /** Trace Reference */ + TraceReference = 115, + /** Complete Request Message */ + CompleteRequestMessage = 116, + /** Globally Unique Temporary Identity */ + Guti = 117, + /** F-Container */ + FContainer = 118, + /** F-Cause */ + FCause = 119, + /** PLMN Identity */ + PlmnId = 120, + /** Target Identification */ + TargetIdentification = 121, + /** Packet Flow ID */ + PacketFlowId = 123, + /** RAB Context */ + RabContext = 124, + /** Source RNC PDCP Context Info */ + SourceRncPdcpContextInfo = 125, + /** Port Number */ + PortNumber = 126, + /** APN Restriction */ + ApnRestriction = 127, + /** Selection Mode */ + SelectionMode = 128, + /** Source Identification */ + SourceIdentification = 129, + /** Change Reporting Action */ + ChangeReportingAction = 131, + /** Fully Qualified PDN Connection Set Identifier */ + FqCsid = 132, + /** Channel Needed */ + ChannelNeeded = 133, + /** eMLPP Priority */ + EmlppPriority = 134, + /** Node Type */ + NodeType = 135, + /** Fully Qualified Domain Name */ + Fqdn = 136, + /** Transaction Identifier */ + Ti = 137, + /** MBMS Session Duration */ + MbmsSessionDuration = 138, + /** MBMS Service Area */ + MbmsServiceArea = 139, + /** MBMS Session Identifier */ + MbmsSessionIdentifier = 140, + /** MBMS Flow Identifier */ + MbmsFlowIdentifier = 141, + /** MBMS IP Multicast Distribution */ + MbmsIpMulticastDistribution = 142, + /** MBMS Distribution Acknowledge */ + MbmsDistributionAcknowledge = 143, + /** RF Selection Priority Index */ + RfspIndex = 144, + /** User CSG Information */ + Uci = 145, + /** CSG Information Reporting Action */ + CsgInformationReportingAction = 146, + /** CSG ID */ + CsgId = 147, + /** CSG Membership Indication */ + Cmi = 148, + /** Service Indicator */ + ServiceIndicator = 149, + /** Detach Type */ + DetachType = 150, + /** Local Distinguished Name */ + Ldn = 151, + /** Node Features */ + NodeFeatures = 152, + /** MBMS Time To Data Transfer */ + MbmsTimeToDataTransfer = 153, + /** Throttling */ + Throttling = 154, + /** Allocation Retention Priority */ + Arp = 155, + /** EPC Timer */ + EpcTimer = 156, + /** Signalling Priority Indication */ + SignallingPriorityIndication = 157, + /** Temporary Mobile Group Identity */ + Tmgi = 158, + /** Additional MM Context For SRVCC */ + AdditionalMmContextForSrvcc = 159, + /** Additional Flags For SRVCC */ + AdditionalFlagsForSrvcc = 160, + /** MDT Configuration */ + MdtConfiguration = 162, + /** Additional Protocol Configuration Options */ + Apco = 163, + /** Absolute Time of MBMS Data Transfer */ + AbsoluteTimeOfMbmsDataTransfer = 164, + /** H(e)NB Information Reporting */ + HenbInformationReporting = 165, + /** IPv4 Configuration Parameters */ + Ipv4ConfigurationParameters = 166, + /** Change To Report Flags */ + ChangeToReportFlags = 167, + /** Action Indication */ + ActionIndication = 168, + /** TWAN Identifier */ + TwanIdentifier = 169, + /** ULI Timestamp */ + UliTimestamp = 170, + /** MBMS Flags */ + MbmsFlags = 171, + /** RAN/NAS Cause */ + RanNasCause = 172, + /** CN Operator Selection Entity */ + CnOperatorSelectionEntity = 173, + /** Trusted WLAN Mode Indication */ + Twmi = 174, + /** Node Number */ + NodeNumber = 175, + /** Node Identifier */ + NodeIdentifier = 176, + /** Presence Reporting Area Action */ + PresenceReportingAreaAction = 177, + /** Presence Reporting Area Information */ + PresenceReportingAreaInformation = 178, + /** TWAN Identifier Timestamp */ + TwanIdentifierTimestamp = 179, + /** Overload Control Information */ + OverloadControlInformation = 180, + /** Load Control Information */ + LoadControlInformation = 181, + /** Metric */ + Metric = 182, + /** Sequence Number */ + SequenceNumber = 183, + /** APN and Relative Capacity */ + ApnAndRelativeCapacity = 184, + /** WLAN Offloadability Indication */ + WlanOffloadabilityIndication = 185, + /** Paging and Service Information */ + PagingAndServiceInformation = 186, + /** Integer Number */ + IntegerNumber = 187, + /** Millisecond Time Stamp */ + MillisecondTimeStamp = 188, + /** Monitoring Event Information */ + MonitoringEventInformation = 189, + /** ECGI List */ + EcgiList = 190, + /** Remote UE Context */ + RemoteUeContext = 191, + /** Remote User ID */ + RemoteUserId = 192, + /** Remote UE IP Information */ + RemoteUeIpInformation = 193, + /** CIoT Optimizations Support Indication */ + CiotOptimizationsSupportIndication = 194, + /** SCEF PDN Connection */ + ScefPdnConnection = 195, + /** Header Compression Configuration */ + HeaderCompressionConfiguration = 196, + /** Extended Protocol Configuration Options */ + ExtendedPco = 197, + /** Serving PLMN Rate Control */ + ServingPlmnRateControl = 198, + /** Counter */ + Counter = 199, + /** Mapped UE Usage Type */ + MappedUeUsageType = 200, + /** Secondary RAT Usage Data Report */ + SecondaryRatUsageDataReport = 201, + /** UP Function Selection Indication Flags */ + UpFunctionSelectionIndicationFlags = 202, + /** Maximum Packet Loss Rate */ + MaximumPacketLossRate = 203, + /** APN Rate Control Status */ + ApnRateControlStatus = 204, + /** Extended Trace Information */ + ExtendedTraceInformation = 205, + /** Monitoring Event Extension Information */ + MonitoringEventExtensionInformation = 206, + /** Additional RRM Policy Index */ + AdditionalRrmPolicyIndex = 207, + /** V2X Context */ + V2xContext = 208, + /** PC5 QoS Parameters */ + Pc5QosParameters = 209, + /** Services Authorized */ + ServicesAuthorized = 210, + /** Bit Rate */ + BitRate = 211, + /** PC5 QoS Flow */ + Pc5QosFlow = 212, + /** SGi PtP Tunnel Address */ + SgiPtpTunnelAddress = 213 + }; + + /** + * A c'tor for this class that gets a pointer to the IE raw data (byte array) + * @param[in] infoElementRawData A pointer to the IE raw data + */ + explicit GtpV2InformationElement(uint8_t* infoElementRawData) : TLVRecord(infoElementRawData) + {} + + ~GtpV2InformationElement() override = default; + + /** + * @return The information element (IE) type + */ + GtpV2InformationElement::Type getIEType(); + + /** + * @return The IE CR flag + */ + uint8_t getCRFlag(); + + /** + * @return The IE instance value + */ + uint8_t getInstance(); + + // implement abstract methods + + size_t getValueOffset() const override + { + return sizeof(uint8_t); + } + + size_t getTotalSize() const override; + + size_t getDataSize() const override; + }; + + /** + * @class GtpV2InformationElementBuilder + * A class for building GTPv2 information elements (IE). This builder receives the IE parameters in its c'tor, + * builds the IE raw buffer and provides a build() method to get a GtpV2InformationElement object out of it + */ + class GtpV2InformationElementBuilder : public TLVRecordBuilder + { + public: + /** + * A c'tor for building information elements (IE) which their value is a byte array. The GtpV2InformationElement + * object can be later retrieved by calling build(). + * @param[in] infoElementType Information elements (IE) type + * @param[in] crFlag CR flag value + * @param[in] instance Instance value + * @param[in] infoElementValue A byte array of the IE value + */ + GtpV2InformationElementBuilder(GtpV2InformationElement::Type infoElementType, const std::bitset<4>& crFlag, + const std::bitset<4>& instance, const std::vector& infoElementValue); + + /** + * Build the GtpV2InformationElement object out of the parameters defined in the c'tor + * @return The GtpV2InformationElement object + */ + GtpV2InformationElement build() const; + + private: + std::bitset<4> m_CRFlag; + std::bitset<4> m_Instance; + }; + + /** + * @class GtpV2Layer + * A class representing the GTPv2 defined in 3GPP TS 29.274 + */ + class GtpV2Layer : public Layer + { + public: + ~GtpV2Layer() override = default; + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + GtpV2Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet, GTPv2) + {} + + /** + * A constructor that creates a new GTPv2 message + * @param messageType GTPv2 message type + * @param sequenceNumber Message sequence number + * @param setTeid Whether or not to set Tunnel Endpoint Identifier in this message + * @param teid Tunnel Endpoint Identifier value. Only used if setTeid is set to true + * @param setMessagePriority Whether or not to set Message Priority in this message + * @param messagePriority Message Priority. Only used if setMessagePriority to true + */ + GtpV2Layer(GtpV2MessageType messageType, uint32_t sequenceNumber, bool setTeid = false, uint32_t teid = 0, + bool setMessagePriority = false, std::bitset<4> messagePriority = 0); + + /** + * A static method that checks whether the port is considered as GTPv2 + * @param[in] port The port number to be checked + * @return True if the port matches those associated with the GTPv2 protocol + */ + static bool isGTPv2Port(uint16_t port) + { + return port == 2123; + } + + /** + * A static method that takes a byte array and detects whether it is a GTPv2 message + * @param[in] data A byte array + * @param[in] dataSize The byte array size (in bytes) + * @return True if the data is identified as GTPv2 message + */ + static bool isDataValid(const uint8_t* data, size_t dataSize); + + /** + * @return The message type + */ + GtpV2MessageType getMessageType() const; + + /** + * Set message type + * @param type The message type to set + */ + void setMessageType(const GtpV2MessageType& type); + + /** + * @return The message length as set in the layer. Note it is different from getHeaderLen() because the later + * refers to the entire layers length, and this property excludes the mandatory part of the GTP-C header + * (the first 4 octets) + */ + uint16_t getMessageLength() const; + + /** + * @return True if there is another GTPv2 message piggybacking on this message (will appear as another + * GtpV2Layer after this layer) + */ + bool isPiggybacking() const; + + /** + * Get the Tunnel Endpoint Identifier (TEID) if exists + * @return A pair of 2 values; the first value states whether TEID exists, and if it's true the second value + * contains the TEID value + */ + std::pair getTeid() const; + + /** + * Set Tunnel Endpoint Identifier (TEID) + * @param teid The TEID value to set + */ + void setTeid(uint32_t teid); + + /** + * Unset Tunnel Endpoint Identifier (TEID) if exists in the layer (otherwise does nothing) + */ + void unsetTeid(); + + /** + * @return The sequence number + */ + uint32_t getSequenceNumber() const; + + /** + * Set the sequence number + * @param sequenceNumber The sequence number value to set + */ + void setSequenceNumber(uint32_t sequenceNumber); + + /** + * Get the Message Property if exists + * @return A pair of 2 values; the first value states whether Message Priority exists, and if it's true + * the second value contains the Message Priority value + */ + std::pair getMessagePriority() const; + + /** + * Set Message Priority + * @param messagePriority The Message Priority value to set + */ + void setMessagePriority(const std::bitset<4>& messagePriority); + + /** + * Unset Message Priority if exists in the layer (otherwise does nothing) + */ + void unsetMessagePriority(); + + /** + * @return The first GTPv2 Information Element (IE). If there are no IEs the returned value will contain + * a logical null (GtpV2InformationElement#isNull() == true) + */ + GtpV2InformationElement getFirstInformationElement() const; + + /** + * Get the GTPv2 Information Element (IE) that comes after a given IE. If the given IE was the last one, the + * returned value will contain a logical null (GtpV2InformationElement#isNull() == true) + * @param[in] infoElement A given GTPv2 Information Element + * @return A GtpV2InformationElement object containing the IE that comes next, or logical null if the given + * IE: (1) is the last one; (2) contains a logical null or (3) doesn't belong to this packet + */ + GtpV2InformationElement getNextInformationElement(GtpV2InformationElement infoElement) const; + + /** + * Get a GTPv2 Information Element (IE) by type + * @param[in] infoElementType GTPv2 Information Element (IE) type + * @return A GtpV2InformationElement object containing the first IE that matches this type, or logical + * null (GtpV2InformationElement#isNull() == true) if no such IE found + */ + GtpV2InformationElement getInformationElement(GtpV2InformationElement::Type infoElementType) const; + + /** + * @return The number of GTPv2 Information Elements (IEs) in this layer + */ + size_t getInformationElementCount() const; + + /** + * Add a new Information Element (IE) at the end of the layer + * @param[in] infoElementBuilder A GtpV2InformationElementBuilder object that contains the requested + * IE data to add + * @return A GtpV2InformationElement object containing the newly added IE data or logical null + * (GtpV2InformationElement#isNull() == true) if addition failed + */ + GtpV2InformationElement addInformationElement(const GtpV2InformationElementBuilder& infoElementBuilder); + + /** + * Add a new Information Element (IE) after an existing one + * @param[in] infoElementBuilder A GtpV2InformationElementBuilder object that contains the requested + * IE data to add + * @param[in] infoElementType The IE type which the newly added option will come after + * @return A GtpV2InformationElement object containing the newly added IE data or logical null + * (GtpV2InformationElement#isNull() == true) if addition failed + */ + GtpV2InformationElement addInformationElementAfter(const GtpV2InformationElementBuilder& infoElementBuilder, + GtpV2InformationElement::Type infoElementType); + + /** + * Remove an existing Information Element (IE) from the layer + * @param[in] infoElementType The IE type to remove + * @return True if the IE was successfully removed or false if type wasn't found or if removal failed + */ + bool removeInformationElement(GtpV2InformationElement::Type infoElementType); + + /** + * Remove all Information Elements (IE) in this layer + * @return True if all IEs were successfully removed or false if removal failed for some reason + */ + bool removeAllInformationElements(); + + // implement abstract methods + + /** + * Identifies if the next layer is GTPv2 piggyback. Otherwise sets PayloadLayer + */ + void parseNextLayer() override; + + /** + * @return The size of the GTPv2 header including its Information Elements (IE) + */ + size_t getHeaderLen() const override; + + /** + * Computes the piggybacking flag by checking if the next layer is also a GTPv2 message + */ + void computeCalculateFields() override; + + std::string toString() const override; + + OsiModelLayer getOsiModelLayer() const override + { + return OsiModelTransportLayer; + } + + private: +#pragma pack(push, 1) + struct gtpv2_basic_header + { +#if (BYTE_ORDER == LITTLE_ENDIAN) + uint8_t unused : 2, messagePriorityPresent : 1, teidPresent : 1, piggybacking : 1, version : 3; +#else + uint8_t version : 3, piggybacking : 1, teidPresent : 1, messagePriorityPresent : 1, unused : 2; +#endif + uint8_t messageType; + uint16_t messageLength; + }; +#pragma pack(pop) + + TLVRecordReader m_IEReader; + + gtpv2_basic_header* getHeader() const + { + return reinterpret_cast(m_Data); + } + + uint8_t* getIEBasePtr() const; + + GtpV2InformationElement addInformationElementAt(const GtpV2InformationElementBuilder& infoElementBuilder, + int offset); + }; } // namespace pcpp diff --git a/Packet++/header/LdapLayer.h b/Packet++/header/LdapLayer.h index e3813ebc3..09e82c9ba 100644 --- a/Packet++/header/LdapLayer.h +++ b/Packet++/header/LdapLayer.h @@ -77,7 +77,7 @@ namespace pcpp // cppcheck-suppress noExplicitConstructor /** * Construct LdapOperationType from Value enum - * @param[in] value the opetation type enum value + * @param[in] value the operation type enum value */ constexpr LdapOperationType(Value value) : m_Value(value) {} diff --git a/Packet++/header/ProtocolType.h b/Packet++/header/ProtocolType.h index bb775fb9f..f853b63f8 100644 --- a/Packet++/header/ProtocolType.h +++ b/Packet++/header/ProtocolType.h @@ -218,9 +218,9 @@ namespace pcpp const ProtocolType GTPv1 = 32; /** - * GTP protocol family (currently only GTPv1) + * GTP protocol family (GTPv1 and GTPv2) */ - const ProtocolTypeFamily GTP = 0x20; + const ProtocolTypeFamily GTP = 0x2039; /** * IEEE 802.3 Ethernet protocol @@ -352,6 +352,11 @@ namespace pcpp */ const ProtocolType WireGuard = 56; + /** + * GTPv2 protocol + */ + const ProtocolType GTPv2 = 57; + /** * An enum representing OSI model layers */ diff --git a/Packet++/header/TLVData.h b/Packet++/header/TLVData.h index d265c925e..b94867edf 100644 --- a/Packet++/header/TLVData.h +++ b/Packet++/header/TLVData.h @@ -23,6 +23,7 @@ namespace pcpp { protected: /** A struct representing the TLV construct */ +#pragma pack(push, 1) struct TLVRawData { /** Record type */ @@ -32,6 +33,7 @@ namespace pcpp /** Record value (variable size) */ uint8_t recordValue[]; }; +#pragma pack(pop) TLVRawData* m_Data; @@ -196,7 +198,7 @@ namespace pcpp return 0; T result; - memcpy(&result, m_Data->recordValue + offset, sizeof(T)); + memcpy(&result, m_Data->recordValue + getValueOffset() + offset, sizeof(T)); return result; } @@ -214,7 +216,7 @@ namespace pcpp if (getDataSize() < sizeof(T)) return false; - memcpy(m_Data->recordValue + valueOffset, &newValue, sizeof(T)); + memcpy(m_Data->recordValue + getValueOffset() + valueOffset, &newValue, sizeof(T)); return true; } @@ -227,6 +229,12 @@ namespace pcpp * @return The size of the record value (meaning the size of the 'V' part in TLV) */ virtual size_t getDataSize() const = 0; + + protected: + virtual size_t getValueOffset() const + { + return 0; + } }; /** diff --git a/Packet++/src/GtpLayer.cpp b/Packet++/src/GtpLayer.cpp index 30113cd5f..2e935cf55 100644 --- a/Packet++/src/GtpLayer.cpp +++ b/Packet++/src/GtpLayer.cpp @@ -113,11 +113,11 @@ namespace pcpp uint8_t nextExtType = getNextExtensionHeaderType(); if (nextExtType > 0 && m_DataLen > totalLength + sizeof(uint8_t)) { - return GtpV1Layer::GtpExtension(m_Data + totalLength, m_DataLen - totalLength, nextExtType); + return { m_Data + totalLength, m_DataLen - totalLength, nextExtType }; } else { - return GtpV1Layer::GtpExtension(); + return {}; } } @@ -134,7 +134,7 @@ namespace pcpp { if (dataLen < 4 * sizeof(uint8_t)) { - return GtpExtension(); + return {}; } data[0] = 1; @@ -142,7 +142,7 @@ namespace pcpp data[2] = content & 0xff; data[3] = 0; - return GtpV1Layer::GtpExtension(data, dataLen, extType); + return { data, dataLen, extType }; } /// ================ @@ -341,11 +341,11 @@ namespace pcpp bool nextExtExists = getNextExtensionHeaderType(nextExtType); if (!nextExtExists || nextExtType == 0 || m_DataLen <= sizeof(gtpv1_header) + sizeof(gtpv1_header_extra)) { - return GtpV1Layer::GtpExtension(); + return {}; } - return GtpV1Layer::GtpExtension(m_Data + sizeof(gtpv1_header) + sizeof(gtpv1_header_extra), - m_DataLen - sizeof(gtpv1_header) - sizeof(gtpv1_header_extra), nextExtType); + return { m_Data + sizeof(gtpv1_header) + sizeof(gtpv1_header_extra), + m_DataLen - sizeof(gtpv1_header) - sizeof(gtpv1_header_extra), nextExtType }; } GtpV1Layer::GtpExtension GtpV1Layer::addExtension(uint8_t extensionType, uint16_t extensionContent) @@ -355,7 +355,7 @@ namespace pcpp if (header == nullptr) { PCPP_LOG_ERROR("Add extension failed: GTP header is nullptr"); - return GtpExtension(); + return {}; } size_t offsetForNewExtension = sizeof(gtpv1_header); @@ -366,7 +366,7 @@ namespace pcpp if (!extendLayer(offsetForNewExtension, sizeof(gtpv1_header_extra))) { PCPP_LOG_ERROR("Add extension failed: cannot extend layer"); - return GtpExtension(); + return {}; } header = getHeader(); } @@ -376,7 +376,7 @@ namespace pcpp if (headerExtra == nullptr) { PCPP_LOG_ERROR("Add extension failed: extra header is nullptr"); - return GtpExtension(); + return {}; } offsetForNewExtension += sizeof(gtpv1_header_extra); @@ -403,7 +403,7 @@ namespace pcpp if (!extendLayer(offsetForNewExtension, 4 * sizeof(uint8_t))) { PCPP_LOG_ERROR("Add extension failed: cannot extend layer"); - return GtpExtension(); + return {}; } // lastExt != null means layer contains 1 or more extensions @@ -524,8 +524,7 @@ namespace pcpp return GTPv1MsgTypeToStringMap.find(0)->second; } - std::unordered_map::const_iterator iter = - GTPv1MsgTypeToStringMap.find(header->messageType); + auto iter = GTPv1MsgTypeToStringMap.find(header->messageType); if (iter != GTPv1MsgTypeToStringMap.end()) { return iter->second; @@ -582,7 +581,7 @@ namespace pcpp // GTP-U message, try to parse the next layer - uint8_t* payload = (uint8_t*)(m_Data + headerLen); + auto* payload = static_cast(m_Data + headerLen); size_t payloadLen = m_DataLen - headerLen; uint8_t subProto = *payload; @@ -675,4 +674,695 @@ namespace pcpp hdr->messageLength = htobe16(m_DataLen - sizeof(gtpv1_header)); } + /// ================ + /// GtpV2MessageType + /// ================ + + struct GtpV2MessageTypeHash + { + size_t operator()(const GtpV2MessageType& messageType) const + { + return static_cast(messageType); + } + }; + + static const std::unordered_map messageTypeMap = { + { GtpV2MessageType::EchoRequest, "Echo Request" }, + { GtpV2MessageType::EchoResponse, "Echo Response" }, + { GtpV2MessageType::VersionNotSupported, "Version Not Supported" }, + { GtpV2MessageType::CreateSessionRequest, "Create Session Request" }, + { GtpV2MessageType::CreateSessionResponse, "Create Session Response" }, + { GtpV2MessageType::ModifyBearerRequest, "Modify Bearer Request" }, + { GtpV2MessageType::ModifyBearerResponse, "Modify Bearer Response" }, + { GtpV2MessageType::DeleteSessionRequest, "Delete Session Request" }, + { GtpV2MessageType::DeleteSessionResponse, "Delete Session Response" }, + { GtpV2MessageType::ChangeNotificationRequest, "Change Notification Request" }, + { GtpV2MessageType::ChangeNotificationResponse, "Change Notification Response" }, + { GtpV2MessageType::RemoteUEReportNotifications, "Remote UE Report Notifications" }, + { GtpV2MessageType::RemoteUEReportAcknowledge, "Remote UE Report Acknowledge" }, + { GtpV2MessageType::ModifyBearerCommand, "Modify Bearer Command" }, + { GtpV2MessageType::ModifyBearerFailure, "Modify Bearer Failure" }, + { GtpV2MessageType::DeleteBearerCommand, "Delete Bearer Command" }, + { GtpV2MessageType::DeleteBearerFailure, "Delete Bearer Failure" }, + { GtpV2MessageType::BearerResourceCommand, "Bearer Resource Command" }, + { GtpV2MessageType::BearerResourceFailure, "Bearer Resource Failure" }, + { GtpV2MessageType::DownlinkDataNotificationFailure, "Downlink Data Notification Failure" }, + { GtpV2MessageType::TraceSessionActivation, "Trace Session Activation" }, + { GtpV2MessageType::TraceSessionDeactivation, "Trace Session Deactivation" }, + { GtpV2MessageType::StopPagingIndication, "Stop Paging Indication" }, + { GtpV2MessageType::CreateBearerRequest, "Create Bearer Request" }, + { GtpV2MessageType::CreateBearerResponse, "Create Bearer Response" }, + { GtpV2MessageType::UpdateBearerRequest, "Update Bearer Request" }, + { GtpV2MessageType::UpdateBearerResponse, "Update Bearer Response" }, + { GtpV2MessageType::DeleteBearerRequest, "Delete Bearer Request" }, + { GtpV2MessageType::DeleteBearerResponse, "Delete Bearer Response" }, + { GtpV2MessageType::DeletePDNRequest, "Delete PDN Request" }, + { GtpV2MessageType::DeletePDNResponse, "Delete PDN Response" }, + { GtpV2MessageType::PGWDownlinkNotification, "PGW Downlink Notification" }, + { GtpV2MessageType::PGWDownlinkAcknowledge, "PGW Downlink Acknowledge" }, + { GtpV2MessageType::IdentificationRequest, "Identification Request" }, + { GtpV2MessageType::IdentificationResponse, "Identification Response" }, + { GtpV2MessageType::ContextRequest, "Context Request" }, + { GtpV2MessageType::ContextResponse, "Context Response" }, + { GtpV2MessageType::ContextAcknowledge, "Context Acknowledge" }, + { GtpV2MessageType::ForwardRelocationRequest, "Forward Relocation Request" }, + { GtpV2MessageType::ForwardRelocationResponse, "Forward Relocation Response" }, + { GtpV2MessageType::ForwardRelocationNotification, "Forward Relocation Notification" }, + { GtpV2MessageType::ForwardRelocationAcknowledge, "Forward Relocation Acknowledge" }, + { GtpV2MessageType::ForwardAccessNotification, "Forward Access Notification" }, + { GtpV2MessageType::ForwardAccessAcknowledge, "Forward Access Acknowledge" }, + { GtpV2MessageType::RelocationCancelRequest, "Relocation Cancel Request" }, + { GtpV2MessageType::RelocationCancelResponse, "Relocation Cancel Response" }, + { GtpV2MessageType::ConfigurationTransferTunnel, "Configuration Transfer Tunnel" }, + { GtpV2MessageType::DetachNotification, "Detach Notification" }, + { GtpV2MessageType::DetachAcknowledge, "Detach Acknowledge" }, + { GtpV2MessageType::CSPaging, "CS Paging" }, + { GtpV2MessageType::RANInformationRelay, "RAN Information Relay" }, + { GtpV2MessageType::AlertMMENotification, "Alert MME Notification" }, + { GtpV2MessageType::AlertMMEAcknowledge, "Alert MME Acknowledge" }, + { GtpV2MessageType::UEActivityNotification, "UE Activity Notification" }, + { GtpV2MessageType::UEActivityAcknowledge, "UE Activity Acknowledge" }, + { GtpV2MessageType::ISRStatus, "ISR Status" }, + { GtpV2MessageType::CreateForwardingRequest, "Create Forwarding Request" }, + { GtpV2MessageType::CreateForwardingResponse, "Create Forwarding Response" }, + { GtpV2MessageType::SuspendNotification, "Suspend Notification" }, + { GtpV2MessageType::SuspendAcknowledge, "Suspend Acknowledge" }, + { GtpV2MessageType::ResumeNotification, "Resume Notification" }, + { GtpV2MessageType::ResumeAcknowledge, "Resume Acknowledge" }, + { GtpV2MessageType::CreateIndirectDataTunnelRequest, "Create Indirect Data Tunnel Request" }, + { GtpV2MessageType::CreateIndirectDataTunnelResponse, "Create Indirect Data Tunnel Response" }, + { GtpV2MessageType::DeleteIndirectDataTunnelRequest, "Delete Indirect Data Tunnel Request" }, + { GtpV2MessageType::DeleteIndirectDataTunnelResponse, "Delete Indirect Data Tunnel Response" }, + { GtpV2MessageType::ReleaseAccessBearersRequest, "Release Access Bearers Request" }, + { GtpV2MessageType::ReleaseAccessBearersResponse, "Release Access Bearers Response" }, + { GtpV2MessageType::DownlinkDataNotification, "Downlink Data Notification" }, + { GtpV2MessageType::DownlinkDataAcknowledge, "Downlink Data Acknowledge" }, + { GtpV2MessageType::PGWRestartNotification, "PGW Restart Notification" }, + { GtpV2MessageType::PGWRestartAcknowledge, "PGW Restart Acknowledge" }, + { GtpV2MessageType::UpdatePDNConnectionRequest, "Update PDN Connection Request" }, + { GtpV2MessageType::UpdatePDNConnectionResponse, "Update PDN Connection Response" }, + { GtpV2MessageType::ModifyAccessBearersRequest, "Modify Access Bearers Request" }, + { GtpV2MessageType::ModifyAccessBearersResponse, "Modify Access Bearers Response" }, + { GtpV2MessageType::MMBSSessionStartRequest, "MMBS Session Start Request" }, + { GtpV2MessageType::MMBSSessionStartResponse, "MMBS Session Start Response" }, + { GtpV2MessageType::MMBSSessionUpdateRequest, "MMBS Session Update Request" }, + { GtpV2MessageType::MMBSSessionUpdateResponse, "MMBS Session Update Response" }, + { GtpV2MessageType::MMBSSessionStopRequest, "MMBS Session Stop Request" }, + { GtpV2MessageType::MMBSSessionStopResponse, "MMBS Session Stop Response" } + }; + + std::string GtpV2MessageType::toString() const + { + auto iter = messageTypeMap.find(m_Value); + if (iter != messageTypeMap.end()) + { + return iter->second; + } + + return "Unknown GTPv2 Message Type"; + } + + // clang-format off + static const std::unordered_map uintToValueMap = { + { static_cast(GtpV2MessageType::EchoRequest), GtpV2MessageType::EchoRequest }, + { static_cast(GtpV2MessageType::EchoResponse), GtpV2MessageType::EchoResponse }, + { static_cast(GtpV2MessageType::VersionNotSupported), GtpV2MessageType::VersionNotSupported }, + { static_cast(GtpV2MessageType::CreateSessionRequest), GtpV2MessageType::CreateSessionRequest }, + { static_cast(GtpV2MessageType::CreateSessionResponse), GtpV2MessageType::CreateSessionResponse }, + { static_cast(GtpV2MessageType::ModifyBearerRequest), GtpV2MessageType::ModifyBearerRequest }, + { static_cast(GtpV2MessageType::ModifyBearerResponse), GtpV2MessageType::ModifyBearerResponse }, + { static_cast(GtpV2MessageType::DeleteSessionRequest), GtpV2MessageType::DeleteSessionRequest }, + { static_cast(GtpV2MessageType::DeleteSessionResponse), GtpV2MessageType::DeleteSessionResponse }, + { static_cast(GtpV2MessageType::ChangeNotificationRequest), GtpV2MessageType::ChangeNotificationRequest }, + { static_cast(GtpV2MessageType::ChangeNotificationResponse), GtpV2MessageType::ChangeNotificationResponse }, + { static_cast(GtpV2MessageType::RemoteUEReportNotifications), GtpV2MessageType::RemoteUEReportNotifications }, + { static_cast(GtpV2MessageType::RemoteUEReportAcknowledge), GtpV2MessageType::RemoteUEReportAcknowledge }, + { static_cast(GtpV2MessageType::ModifyBearerCommand), GtpV2MessageType::ModifyBearerCommand }, + { static_cast(GtpV2MessageType::ModifyBearerFailure), GtpV2MessageType::ModifyBearerFailure }, + { static_cast(GtpV2MessageType::DeleteBearerCommand), GtpV2MessageType::DeleteBearerCommand }, + { static_cast(GtpV2MessageType::DeleteBearerFailure), GtpV2MessageType::DeleteBearerFailure }, + { static_cast(GtpV2MessageType::BearerResourceCommand), GtpV2MessageType::BearerResourceCommand }, + { static_cast(GtpV2MessageType::BearerResourceFailure), GtpV2MessageType::BearerResourceFailure }, + { static_cast(GtpV2MessageType::DownlinkDataNotificationFailure), GtpV2MessageType::DownlinkDataNotificationFailure }, + { static_cast(GtpV2MessageType::TraceSessionActivation), GtpV2MessageType::TraceSessionActivation }, + { static_cast(GtpV2MessageType::TraceSessionDeactivation), GtpV2MessageType::TraceSessionDeactivation }, + { static_cast(GtpV2MessageType::StopPagingIndication), GtpV2MessageType::StopPagingIndication }, + { static_cast(GtpV2MessageType::CreateBearerRequest), GtpV2MessageType::CreateBearerRequest }, + { static_cast(GtpV2MessageType::CreateBearerResponse), GtpV2MessageType::CreateBearerResponse }, + { static_cast(GtpV2MessageType::UpdateBearerRequest), GtpV2MessageType::UpdateBearerRequest }, + { static_cast(GtpV2MessageType::UpdateBearerResponse), GtpV2MessageType::UpdateBearerResponse }, + { static_cast(GtpV2MessageType::DeleteBearerRequest), GtpV2MessageType::DeleteBearerRequest }, + { static_cast(GtpV2MessageType::DeleteBearerResponse), GtpV2MessageType::DeleteBearerResponse }, + { static_cast(GtpV2MessageType::DeletePDNRequest), GtpV2MessageType::DeletePDNRequest }, + { static_cast(GtpV2MessageType::DeletePDNResponse), GtpV2MessageType::DeletePDNResponse }, + { static_cast(GtpV2MessageType::PGWDownlinkNotification), GtpV2MessageType::PGWDownlinkNotification }, + { static_cast(GtpV2MessageType::PGWDownlinkAcknowledge), GtpV2MessageType::PGWDownlinkAcknowledge }, + { static_cast(GtpV2MessageType::IdentificationRequest), GtpV2MessageType::IdentificationRequest }, + { static_cast(GtpV2MessageType::IdentificationResponse), GtpV2MessageType::IdentificationResponse }, + { static_cast(GtpV2MessageType::ContextRequest), GtpV2MessageType::ContextRequest }, + { static_cast(GtpV2MessageType::ContextResponse), GtpV2MessageType::ContextResponse }, + { static_cast(GtpV2MessageType::ContextAcknowledge), GtpV2MessageType::ContextAcknowledge }, + { static_cast(GtpV2MessageType::ForwardRelocationRequest), GtpV2MessageType::ForwardRelocationRequest }, + { static_cast(GtpV2MessageType::ForwardRelocationResponse), GtpV2MessageType::ForwardRelocationResponse }, + { static_cast(GtpV2MessageType::ForwardRelocationNotification), GtpV2MessageType::ForwardRelocationNotification }, + { static_cast(GtpV2MessageType::ForwardRelocationAcknowledge), GtpV2MessageType::ForwardRelocationAcknowledge }, + { static_cast(GtpV2MessageType::ForwardAccessNotification), GtpV2MessageType::ForwardAccessNotification }, + { static_cast(GtpV2MessageType::ForwardAccessAcknowledge), GtpV2MessageType::ForwardAccessAcknowledge }, + { static_cast(GtpV2MessageType::RelocationCancelRequest), GtpV2MessageType::RelocationCancelRequest }, + { static_cast(GtpV2MessageType::RelocationCancelResponse), GtpV2MessageType::RelocationCancelResponse }, + { static_cast(GtpV2MessageType::ConfigurationTransferTunnel), GtpV2MessageType::ConfigurationTransferTunnel }, + { static_cast(GtpV2MessageType::DetachNotification), GtpV2MessageType::DetachNotification }, + { static_cast(GtpV2MessageType::DetachAcknowledge), GtpV2MessageType::DetachAcknowledge }, + { static_cast(GtpV2MessageType::CSPaging), GtpV2MessageType::CSPaging }, + { static_cast(GtpV2MessageType::RANInformationRelay), GtpV2MessageType::RANInformationRelay }, + { static_cast(GtpV2MessageType::AlertMMENotification), GtpV2MessageType::AlertMMENotification }, + { static_cast(GtpV2MessageType::AlertMMEAcknowledge), GtpV2MessageType::AlertMMEAcknowledge }, + { static_cast(GtpV2MessageType::UEActivityNotification), GtpV2MessageType::UEActivityNotification }, + { static_cast(GtpV2MessageType::UEActivityAcknowledge), GtpV2MessageType::UEActivityAcknowledge }, + { static_cast(GtpV2MessageType::ISRStatus), GtpV2MessageType::ISRStatus }, + { static_cast(GtpV2MessageType::CreateForwardingRequest), GtpV2MessageType::CreateForwardingRequest }, + { static_cast(GtpV2MessageType::CreateForwardingResponse), GtpV2MessageType::CreateForwardingResponse }, + { static_cast(GtpV2MessageType::SuspendNotification), GtpV2MessageType::SuspendNotification }, + { static_cast(GtpV2MessageType::SuspendAcknowledge), GtpV2MessageType::SuspendAcknowledge }, + { static_cast(GtpV2MessageType::ResumeNotification), GtpV2MessageType::ResumeNotification }, + { static_cast(GtpV2MessageType::ResumeAcknowledge), GtpV2MessageType::ResumeAcknowledge }, + { static_cast(GtpV2MessageType::CreateIndirectDataTunnelRequest), GtpV2MessageType::CreateIndirectDataTunnelRequest }, + { static_cast(GtpV2MessageType::CreateIndirectDataTunnelResponse), GtpV2MessageType::CreateIndirectDataTunnelResponse }, + { static_cast(GtpV2MessageType::DeleteIndirectDataTunnelRequest), GtpV2MessageType::DeleteIndirectDataTunnelRequest }, + { static_cast(GtpV2MessageType::DeleteIndirectDataTunnelResponse), GtpV2MessageType::DeleteIndirectDataTunnelResponse }, + { static_cast(GtpV2MessageType::ReleaseAccessBearersRequest), GtpV2MessageType::ReleaseAccessBearersRequest }, + { static_cast(GtpV2MessageType::ReleaseAccessBearersResponse), GtpV2MessageType::ReleaseAccessBearersResponse }, + { static_cast(GtpV2MessageType::DownlinkDataNotification), GtpV2MessageType::DownlinkDataNotification }, + { static_cast(GtpV2MessageType::DownlinkDataAcknowledge), GtpV2MessageType::DownlinkDataAcknowledge }, + { static_cast(GtpV2MessageType::PGWRestartNotification), GtpV2MessageType::PGWRestartNotification }, + { static_cast(GtpV2MessageType::PGWRestartAcknowledge), GtpV2MessageType::PGWRestartAcknowledge }, + { static_cast(GtpV2MessageType::UpdatePDNConnectionRequest), GtpV2MessageType::UpdatePDNConnectionRequest }, + { static_cast(GtpV2MessageType::UpdatePDNConnectionResponse), GtpV2MessageType::UpdatePDNConnectionResponse }, + { static_cast(GtpV2MessageType::ModifyAccessBearersRequest), GtpV2MessageType::ModifyAccessBearersRequest }, + { static_cast(GtpV2MessageType::ModifyAccessBearersResponse), GtpV2MessageType::ModifyAccessBearersResponse }, + { static_cast(GtpV2MessageType::MMBSSessionStartRequest), GtpV2MessageType::MMBSSessionStartRequest }, + { static_cast(GtpV2MessageType::MMBSSessionStartResponse), GtpV2MessageType::MMBSSessionStartResponse }, + { static_cast(GtpV2MessageType::MMBSSessionUpdateRequest), GtpV2MessageType::MMBSSessionUpdateRequest }, + { static_cast(GtpV2MessageType::MMBSSessionUpdateResponse), GtpV2MessageType::MMBSSessionUpdateResponse }, + { static_cast(GtpV2MessageType::MMBSSessionStopRequest), GtpV2MessageType::MMBSSessionStopRequest }, + { static_cast(GtpV2MessageType::MMBSSessionStopResponse), GtpV2MessageType::MMBSSessionStopResponse } + }; + // clang-format on + + GtpV2MessageType GtpV2MessageType::fromUintValue(uint8_t value) + { + auto iter = uintToValueMap.find(value); + if (iter != uintToValueMap.end()) + { + return iter->second; + } + + return Unknown; + } + + /// ======================= + /// GtpV2InformationElement + /// ======================= + + GtpV2InformationElement::Type GtpV2InformationElement::getIEType() + { + if (m_Data == nullptr) + { + return GtpV2InformationElement::Type::Unknown; + } + + auto ieType = m_Data->recordType; + if ((ieType >= 4 && ieType <= 50) || (ieType >= 52 && ieType <= 70) || ieType == 98 || ieType == 101 || + ieType == 102 || ieType == 122 || ieType == 130 || ieType == 161 || ieType > 213) + { + return GtpV2InformationElement::Type::Unknown; + } + + return static_cast(ieType); + } + + uint8_t GtpV2InformationElement::getCRFlag() + { + if (m_Data == nullptr) + { + return 0; + } + + return m_Data->recordValue[0] >> 4; + } + + uint8_t GtpV2InformationElement::getInstance() + { + if (m_Data == nullptr) + { + return 0; + } + + return m_Data->recordValue[0] & 0xf; + } + + size_t GtpV2InformationElement::getTotalSize() const + { + if (m_Data == nullptr) + { + return 0; + } + + return getDataSize() + 2 * sizeof(uint8_t) + sizeof(uint16_t); + } + + size_t GtpV2InformationElement::getDataSize() const + { + if (m_Data == nullptr) + { + return 0; + } + + return static_cast(be16toh(m_Data->recordLen)); + } + + /// ============================== + /// GtpV2InformationElementBuilder + /// ============================== + + GtpV2InformationElementBuilder::GtpV2InformationElementBuilder(GtpV2InformationElement::Type infoElementType, + const std::bitset<4>& crFlag, + const std::bitset<4>& instance, + const std::vector& infoElementValue) + : TLVRecordBuilder(static_cast(infoElementType), infoElementValue.data(), + static_cast(infoElementValue.size())), + m_CRFlag(crFlag), m_Instance(instance) + {} + + GtpV2InformationElement GtpV2InformationElementBuilder::build() const + { + if (m_RecType == 0) + { + GtpV2InformationElement(nullptr); + } + + size_t infoElementBaseSize = sizeof(uint8_t) + sizeof(uint16_t); + size_t infoElementTotalSize = infoElementBaseSize + sizeof(uint8_t) + m_RecValueLen; + auto* recordBuffer = new uint8_t[infoElementTotalSize]; + recordBuffer[0] = static_cast(m_RecType); + auto infoElementLength = htobe16(m_RecValueLen); + memcpy(recordBuffer + sizeof(uint8_t), &infoElementLength, sizeof(uint16_t)); + auto crFlag = static_cast(m_CRFlag.to_ulong()); + auto instance = static_cast(m_Instance.to_ulong()); + recordBuffer[infoElementBaseSize] = ((crFlag << 4) & 0xf0) | (instance & 0x0f); + if (m_RecValueLen > 0 && m_RecValue != nullptr) + { + memcpy(recordBuffer + infoElementBaseSize + sizeof(uint8_t), m_RecValue, m_RecValueLen); + } + + return GtpV2InformationElement(recordBuffer); + } + + /// ========== + /// GtpV2Layer + /// ========== + + GtpV2Layer::GtpV2Layer(GtpV2MessageType messageType, uint32_t sequenceNumber, bool setTeid, uint32_t teid, + bool setMessagePriority, std::bitset<4> messagePriority) + { + size_t messageLength = sizeof(uint32_t) + (setTeid ? sizeof(uint32_t) : 0); + size_t headerLen = sizeof(gtpv2_basic_header) + messageLength; + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + + auto* hdr = getHeader(); + hdr->version = 2; + hdr->teidPresent = setTeid; + hdr->messagePriorityPresent = setMessagePriority; + hdr->messageType = static_cast(messageType); + hdr->messageLength = htobe16(messageLength); + + auto* dataPtr = m_Data + sizeof(gtpv2_basic_header); + if (setTeid) + { + teid = htobe32(teid); + memcpy(dataPtr, &teid, sizeof(uint32_t)); + dataPtr += sizeof(uint32_t); + } + + sequenceNumber = htobe32(sequenceNumber) >> 8; + memcpy(dataPtr, &sequenceNumber, sizeof(uint32_t)); + dataPtr += sizeof(uint32_t) - 1; + + if (setMessagePriority) + { + auto messagePriorityNum = static_cast(messagePriority.to_ulong()); + dataPtr[0] = messagePriorityNum << 4; + } + + m_Protocol = GTPv2; + } + + bool GtpV2Layer::isDataValid(const uint8_t* data, size_t dataSize) + { + if (!data || dataSize < sizeof(gtpv2_basic_header) + sizeof(uint32_t)) + { + return false; + } + + auto* header = reinterpret_cast(data); + + if (header->version != 2) + { + return false; + } + + return true; + } + + GtpV2MessageType GtpV2Layer::getMessageType() const + { + return GtpV2MessageType::fromUintValue(getHeader()->messageType); + } + + void GtpV2Layer::setMessageType(const GtpV2MessageType& type) + { + getHeader()->messageType = type; + } + + uint16_t GtpV2Layer::getMessageLength() const + { + return be16toh(getHeader()->messageLength); + } + + bool GtpV2Layer::isPiggybacking() const + { + return getHeader()->piggybacking; + } + + std::pair GtpV2Layer::getTeid() const + { + if (!getHeader()->teidPresent) + { + return { false, 0 }; + } + + return { true, be32toh(*reinterpret_cast(m_Data + sizeof(gtpv2_basic_header))) }; + } + + void GtpV2Layer::setTeid(uint32_t teid) + { + auto* header = getHeader(); + + auto teidOffset = sizeof(gtpv2_basic_header); + if (!header->teidPresent) + { + if (!extendLayer(static_cast(teidOffset), sizeof(uint32_t))) + { + PCPP_LOG_ERROR("Unable to set TEID: failed to extend the layer"); + return; + } + header = getHeader(); + header->messageLength = htobe16(be16toh(header->messageLength) + sizeof(uint32_t)); + } + + reinterpret_cast(m_Data + teidOffset)[0] = htobe32(teid); + + header->teidPresent = 1; + } + + void GtpV2Layer::unsetTeid() + { + auto* header = getHeader(); + + if (!header->teidPresent) + { + return; + } + + auto teidOffset = sizeof(gtpv2_basic_header); + if (!shortenLayer(static_cast(teidOffset), sizeof(uint32_t))) + { + PCPP_LOG_ERROR("Unable to unset TEID: failed to shorten the layer"); + return; + } + + header = getHeader(); + header->messageLength = htobe16(be16toh(header->messageLength) - sizeof(uint32_t)); + header->teidPresent = 0; + } + + uint32_t GtpV2Layer::getSequenceNumber() const + { + auto* sequencePos = m_Data + sizeof(gtpv2_basic_header); + if (getHeader()->teidPresent) + { + sequencePos += sizeof(uint32_t); + } + + return be32toh(*reinterpret_cast(sequencePos)) >> 8; + } + + void GtpV2Layer::setSequenceNumber(uint32_t sequenceNumber) + { + auto* sequencePos = m_Data + sizeof(gtpv2_basic_header); + if (getHeader()->teidPresent) + { + sequencePos += sizeof(uint32_t); + } + + sequenceNumber = htobe32(sequenceNumber) >> 8; + memcpy(sequencePos, &sequenceNumber, sizeof(uint32_t) - 1); + } + + std::pair GtpV2Layer::getMessagePriority() const + { + auto* header = getHeader(); + + if (!header->messagePriorityPresent) + { + return { false, 0 }; + } + + auto* mpPos = m_Data + sizeof(gtpv2_basic_header) + sizeof(uint32_t) - 1; + if (header->teidPresent) + { + mpPos += sizeof(uint32_t); + } + + return { true, mpPos[0] >> 4 }; + } + + void GtpV2Layer::setMessagePriority(const std::bitset<4>& messagePriority) + { + auto* header = getHeader(); + + header->messagePriorityPresent = 1; + + auto* mpPos = m_Data + sizeof(gtpv2_basic_header) + sizeof(uint32_t) - 1; + if (header->teidPresent) + { + mpPos += sizeof(uint32_t); + } + + auto messagePriorityNum = static_cast(messagePriority.to_ulong()); + mpPos[0] = messagePriorityNum << 4; + } + + void GtpV2Layer::unsetMessagePriority() + { + auto* header = getHeader(); + + header->messagePriorityPresent = 0; + + auto* mpPos = m_Data + sizeof(gtpv2_basic_header) + sizeof(uint32_t) - 1; + if (header->teidPresent) + { + mpPos += sizeof(uint32_t); + } + + mpPos[0] = 0; + } + + GtpV2InformationElement GtpV2Layer::getFirstInformationElement() const + { + auto* basePtr = getIEBasePtr(); + return m_IEReader.getFirstTLVRecord(basePtr, m_Data + getHeaderLen() - basePtr); + } + + GtpV2InformationElement GtpV2Layer::getNextInformationElement(GtpV2InformationElement infoElement) const + { + auto* basePtr = getIEBasePtr(); + return m_IEReader.getNextTLVRecord(infoElement, basePtr, m_Data + getHeaderLen() - basePtr); + } + + GtpV2InformationElement GtpV2Layer::getInformationElement(GtpV2InformationElement::Type infoElementType) const + { + auto* basePtr = getIEBasePtr(); + return m_IEReader.getTLVRecord(static_cast(infoElementType), basePtr, + m_Data + getHeaderLen() - basePtr); + } + + size_t GtpV2Layer::getInformationElementCount() const + { + auto* basePtr = getIEBasePtr(); + return m_IEReader.getTLVRecordCount(basePtr, m_Data + getHeaderLen() - basePtr); + } + + GtpV2InformationElement GtpV2Layer::addInformationElement(const GtpV2InformationElementBuilder& infoElementBuilder) + { + return addInformationElementAt(infoElementBuilder, static_cast(getHeaderLen())); + } + + GtpV2InformationElement GtpV2Layer::addInformationElementAfter( + const GtpV2InformationElementBuilder& infoElementBuilder, GtpV2InformationElement::Type infoElementType) + { + auto prevInfoElement = getInformationElement(infoElementType); + + if (prevInfoElement.isNull()) + { + PCPP_LOG_ERROR("Information element type " << static_cast(infoElementType) + << " doesn't exist in layer"); + return GtpV2InformationElement(nullptr); + } + auto offset = prevInfoElement.getRecordBasePtr() + prevInfoElement.getTotalSize() - m_Data; + return addInformationElementAt(infoElementBuilder, offset); + } + + bool GtpV2Layer::removeInformationElement(GtpV2InformationElement::Type infoElementType) + { + auto infoElementToRemove = getInformationElement(infoElementType); + if (infoElementToRemove.isNull()) + { + return false; + } + + int offset = infoElementToRemove.getRecordBasePtr() - m_Data; + + auto infoElementSize = infoElementToRemove.getTotalSize(); + if (!shortenLayer(offset, infoElementSize)) + { + return false; + } + + getHeader()->messageLength = htobe16(be16toh(getHeader()->messageLength) - infoElementSize); + m_IEReader.changeTLVRecordCount(-1); + return true; + } + + bool GtpV2Layer::removeAllInformationElements() + { + auto firstInfoElement = getFirstInformationElement(); + if (firstInfoElement.isNull()) + { + return true; + } + + auto offset = firstInfoElement.getRecordBasePtr() - m_Data; + + if (!shortenLayer(offset, getHeaderLen() - offset)) + { + return false; + } + + m_IEReader.changeTLVRecordCount(static_cast(0 - getInformationElementCount())); + return true; + } + + GtpV2InformationElement GtpV2Layer::addInformationElementAt( + const GtpV2InformationElementBuilder& infoElementBuilder, int offset) + { + auto newInfoElement = infoElementBuilder.build(); + + if (newInfoElement.isNull()) + { + PCPP_LOG_ERROR("Cannot build new information element"); + return newInfoElement; + } + + auto sizeToExtend = newInfoElement.getTotalSize(); + + if (!extendLayer(offset, sizeToExtend)) + { + PCPP_LOG_ERROR("Could not extend GtpV2Layer in [" << sizeToExtend << "] bytes"); + newInfoElement.purgeRecordData(); + return GtpV2InformationElement(nullptr); + } + + memcpy(m_Data + offset, newInfoElement.getRecordBasePtr(), newInfoElement.getTotalSize()); + + auto newMessageLength = getMessageLength() + newInfoElement.getTotalSize(); + + newInfoElement.purgeRecordData(); + + m_IEReader.changeTLVRecordCount(1); + + getHeader()->messageLength = htobe16(newMessageLength); + + uint8_t* newInfoElementPtr = m_Data + offset; + + return GtpV2InformationElement(newInfoElementPtr); + } + + void GtpV2Layer::parseNextLayer() + { + auto headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + { + return; + } + + auto* nextLayerData = m_Data + headerLen; + auto nextLayerDataLen = m_DataLen - headerLen; + + if (getHeader()->piggybacking && GtpV2Layer::isDataValid(nextLayerData, nextLayerDataLen)) + { + m_NextLayer = new GtpV2Layer(nextLayerData, nextLayerDataLen, this, m_Packet); + } + else + { + m_NextLayer = new PayloadLayer(nextLayerData, nextLayerDataLen, this, m_Packet); + } + } + + size_t GtpV2Layer::getHeaderLen() const + { + auto messageLength = be16toh(getHeader()->messageLength) + sizeof(gtpv2_basic_header); + if (messageLength > m_DataLen) + { + return m_DataLen; + } + + return messageLength; + } + + void GtpV2Layer::computeCalculateFields() + { + if (m_NextLayer == nullptr) + { + return; + } + + if (m_NextLayer->getProtocol() == GTPv2) + { + getHeader()->piggybacking = 1; + } + else + { + getHeader()->piggybacking = 0; + } + } + + std::string GtpV2Layer::toString() const + { + return "GTPv2 Layer, " + getMessageType().toString() + " message"; + } + + uint8_t* GtpV2Layer::getIEBasePtr() const + { + auto* basePtr = m_Data + sizeof(gtpv2_basic_header) + sizeof(uint32_t); + if (getHeader()->teidPresent) + { + basePtr += sizeof(uint32_t); + } + + return basePtr; + } + } // namespace pcpp diff --git a/Packet++/src/TcpLayer.cpp b/Packet++/src/TcpLayer.cpp index d146efd58..45d1a1e14 100644 --- a/Packet++/src/TcpLayer.cpp +++ b/Packet++/src/TcpLayer.cpp @@ -17,6 +17,7 @@ #include "SomeIpLayer.h" #include "SmtpLayer.h" #include "LdapLayer.h" +#include "GtpLayer.h" #include "PacketUtils.h" #include "Logger.h" #include "DeprecationUtils.h" @@ -418,6 +419,9 @@ namespace pcpp if (!m_NextLayer) m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); } + else if ((GtpV2Layer::isGTPv2Port(portDst) || GtpV2Layer::isGTPv2Port(portSrc)) && + GtpV2Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new GtpV2Layer(payload, payloadLen, this, m_Packet); else m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); } diff --git a/Packet++/src/UdpLayer.cpp b/Packet++/src/UdpLayer.cpp index 6261a5442..9cfaa2cbe 100644 --- a/Packet++/src/UdpLayer.cpp +++ b/Packet++/src/UdpLayer.cpp @@ -124,6 +124,9 @@ namespace pcpp else if ((GtpV1Layer::isGTPv1Port(portDst) || GtpV1Layer::isGTPv1Port(portSrc)) && GtpV1Layer::isGTPv1(udpData, udpDataLen)) m_NextLayer = new GtpV1Layer(udpData, udpDataLen, this, m_Packet); + else if ((GtpV2Layer::isGTPv2Port(portDst) || GtpV2Layer::isGTPv2Port(portSrc)) && + GtpV2Layer::isDataValid(udpData, udpDataLen)) + m_NextLayer = new GtpV2Layer(udpData, udpDataLen, this, m_Packet); else if ((DhcpV6Layer::isDhcpV6Port(portSrc) || DhcpV6Layer::isDhcpV6Port(portDst)) && (DhcpV6Layer::isDataValid(udpData, udpDataLen))) m_NextLayer = new DhcpV6Layer(udpData, udpDataLen, this, m_Packet); diff --git a/README.md b/README.md index 5faa6f202..5bd05a0b9 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,7 @@ PcapPlusPlus currently supports parsing, editing and creation of packets of the ### Transport Layer (L4) 26. COTP -27. GTP (v1) +27. GTP (v1 & v2) 28. IPSec AH & ESP - parsing only (no editing capabilities) 29. TCP 30. TPKT diff --git a/Tests/Packet++Test/PacketExamples/gtpv2-no-info-elements.dat b/Tests/Packet++Test/PacketExamples/gtpv2-no-info-elements.dat new file mode 100644 index 000000000..986e1a4e7 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gtpv2-no-info-elements.dat @@ -0,0 +1 @@ +40b4f0d8d93c288a1ccb07d9080045680028ccbe00003d117b5e6f47ec31ddb1fc15084b084b0014273b4c22002ad37d15901a4a4310 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gtpv2-non-zero-cf-flag-instance.dat b/Tests/Packet++Test/PacketExamples/gtpv2-non-zero-cf-flag-instance.dat new file mode 100644 index 000000000..584d18eef --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gtpv2-non-zero-cf-flag-instance.dat @@ -0,0 +1 @@ +40b4f0d8d93c288a1ccb07d9080045680039ccbe00003d117b4d6f47ec31ddb1fc15084b084b002509a94c220019d37d15901a4a431056000d7c1864f6292e1864f62901ce6621 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gtpv2-over-tcp.dat b/Tests/Packet++Test/PacketExamples/gtpv2-over-tcp.dat new file mode 100644 index 000000000..cecf90454 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gtpv2-over-tcp.dat @@ -0,0 +1 @@ +08b4b11a46ad105badb0f507080045000039000100004006f641c0a80164c0a801c8084b084b000000000000000050022000646000004825000900000000003039000300010011 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gtpv2-with-piggyback-malformed.dat b/Tests/Packet++Test/PacketExamples/gtpv2-with-piggyback-malformed.dat new file mode 100644 index 000000000..0cc78f6b3 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gtpv2-with-piggyback-malformed.dat @@ -0,0 +1 @@ +08b4b11a46ad105badb0f50708004500002b000100004011f644c0a80164c0a801c8084b084b00179a41500100090030390003000100113132 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gtpv2-with-piggyback.dat b/Tests/Packet++Test/PacketExamples/gtpv2-with-piggyback.dat new file mode 100644 index 000000000..7ccee5052 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gtpv2-with-piggyback.dat @@ -0,0 +1 @@ +08b4b11a46ad105badb0f507080045000041000100004011f62ec0a80164c0a801c8084b084b002d92d3500100090030390003000100114c21001400015666010932900100080033879334495183f6 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gtpv2-with-teid.dat b/Tests/Packet++Test/PacketExamples/gtpv2-with-teid.dat new file mode 100644 index 000000000..85fe07f11 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gtpv2-with-teid.dat @@ -0,0 +1 @@ +40b4f0d8d93c288a1ccb07d908004568008bccbe00003d117afb6f47ec31ddb1fc15084b084b007700004822006bd37d15901a4a430056000d001864f6292e1864f62901ce66215300030064f62952000100065700090086a43ed0306f47ec314800080000000800000008004b00080053028970726123607200020023005d00120049000100055700090184a430f3e26f47ec430300010012 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gtpv2.pcap b/Tests/Packet++Test/PacketExamples/gtpv2.pcap new file mode 100644 index 0000000000000000000000000000000000000000..316543da88e2672fcb6202ff1087cbd4be3ab73d GIT binary patch literal 1184 zcma)4PfXKb6n`BsHWCa&h)O&Z$Z!Lb8u7U-IjF`M&pizxStg_4Rev zDS)w_t`;D#Bc$eQ#JTu4Cf({PF-O|mof=KpK;Tim-8ZNE^j@IM`oCi;0yY>Q%p4AMkWB6 zH<@)4f>`o3ebXLvm&+A7X59bYk%|e&ir7NbAhJvibS{7W^#px_-pwDMc6(g+j=!nh zwZZuW+<*HL;EZGOcY5%f@AJl=X4{Bu1m*#T+F)k;R`W{Bg?^ZXdN?X(-Cpvc-p5-f zaFFO;#;&Yku*2QevmY3eVF+xfvX2q86P9I77uq8_SfCxQK_d(!rv^FHomJ-N7v%I; z;Y}m5NX?{n4PilWPAfc~-g$5leFEF&M$5o@!`1Z{d$m|QKpzsJ*OB$UTBAh7>wN8= zhuh;6*2ILcND3UItvD5GGEUjq6$(r8FFoRhFWt`Ob%yavytZ?2hk^}& -PTF_TEST_CASE(GtpLayerParsingTest) +PTF_TEST_CASE(GtpV1LayerParsingTest) { timeval time; gettimeofday(&time, nullptr); @@ -161,7 +162,7 @@ PTF_TEST_CASE(GtpLayerParsingTest) PTF_ASSERT_FALSE(gtpLayer->isGTPUMessage()); } // GtpLayerParsingTest -PTF_TEST_CASE(GtpLayerCreationTest) +PTF_TEST_CASE(GtpV1LayerCreationTest) { timeval time; gettimeofday(&time, nullptr); @@ -230,7 +231,7 @@ PTF_TEST_CASE(GtpLayerCreationTest) newGtpPacket.getRawPacket()->getRawDataLen()); } // GtpLayerCreationTest -PTF_TEST_CASE(GtpLayerEditTest) +PTF_TEST_CASE(GtpV1LayerEditTest) { timeval time; gettimeofday(&time, nullptr); @@ -282,3 +283,355 @@ PTF_TEST_CASE(GtpLayerEditTest) delete[] buffer2; } // GtpLayerEditTest + +PTF_TEST_CASE(GtpV2LayerParsingTest) +{ + timeval time{}; + gettimeofday(&time, nullptr); + + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gtpv2-with-teid.dat"); + pcpp::Packet gtpPacket(&rawPacket1); + + PTF_ASSERT_TRUE(gtpPacket.isPacketOfType(pcpp::GTPv2)); + PTF_ASSERT_TRUE(gtpPacket.isPacketOfType(pcpp::GTP)); + auto gtpLayer = gtpPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(gtpLayer); + + PTF_ASSERT_EQUAL(gtpLayer->getMessageType(), pcpp::GtpV2MessageType::ModifyBearerRequest); + PTF_ASSERT_EQUAL(gtpLayer->getMessageLength(), 107); + PTF_ASSERT_FALSE(gtpLayer->isPiggybacking()); + PTF_ASSERT_EQUAL(gtpLayer->getHeaderLen(), 111); + auto teid = gtpLayer->getTeid(); + PTF_ASSERT_TRUE(teid.first); + PTF_ASSERT_EQUAL(teid.second, 0xd37d1590); + PTF_ASSERT_EQUAL(gtpLayer->getSequenceNumber(), 0x1a4a43); + PTF_ASSERT_FALSE(gtpLayer->getMessagePriority().first); + PTF_ASSERT_EQUAL(gtpLayer->toString(), "GTPv2 Layer, Modify Bearer Request message"); + PTF_ASSERT_NULL(gtpLayer->getNextLayer()); + + PTF_ASSERT_EQUAL(gtpLayer->getInformationElementCount(), 9); + + auto expectedIEValues = std::vector< + std::tuple>{ + { pcpp::GtpV2InformationElement::Type::Uli, 17, 13, 0, 0, 0x18, 0x64 }, + { pcpp::GtpV2InformationElement::Type::ServingNetwork, 7, 3, 0, 0, 0x64, 0xf6 }, + { pcpp::GtpV2InformationElement::Type::RatType, 5, 1, 0, 0, 0x06, 0 }, + { pcpp::GtpV2InformationElement::Type::FTeid, 13, 9, 0, 0, 0x86, 0xa4 }, + { pcpp::GtpV2InformationElement::Type::Ambr, 12, 8, 0, 0, 0, 0 }, + { pcpp::GtpV2InformationElement::Type::Mei, 12, 8, 0, 0, 0x53, 0x02 }, + { pcpp::GtpV2InformationElement::Type::UeTimeZone, 6, 2, 0, 0, 0x23, 0 }, + { pcpp::GtpV2InformationElement::Type::BearerContext, 22, 18, 0, 0, 0x49, 0 }, + { pcpp::GtpV2InformationElement::Type::Recovery, 5, 1, 0, 0, 18, 0 }, + }; + + auto infoElement = gtpLayer->getFirstInformationElement(); + for (auto expectedIEValue : expectedIEValues) + { + PTF_ASSERT_EQUAL(infoElement.getIEType(), std::get<0>(expectedIEValue), enumclass); + PTF_ASSERT_EQUAL(infoElement.getTotalSize(), std::get<1>(expectedIEValue)); + PTF_ASSERT_EQUAL(infoElement.getDataSize(), std::get<2>(expectedIEValue)); + PTF_ASSERT_EQUAL(static_cast(infoElement.getCRFlag()), static_cast(std::get<3>(expectedIEValue))); + PTF_ASSERT_EQUAL(static_cast(infoElement.getInstance()), + static_cast(std::get<4>(expectedIEValue))); + PTF_ASSERT_EQUAL(infoElement.getValueAs(), std::get<5>(expectedIEValue)); + if (infoElement.getDataSize() > 1) + { + PTF_ASSERT_EQUAL(static_cast(infoElement.getValueAs(1)), + static_cast(std::get<6>(expectedIEValue))); + } + PTF_ASSERT_EQUAL(gtpLayer->getInformationElement(infoElement.getIEType()).getIEType(), + std::get<0>(expectedIEValue), enumclass); + infoElement = gtpLayer->getNextInformationElement(infoElement); + } + PTF_ASSERT_TRUE(infoElement.isNull()); + } + + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gtpv2-with-piggyback.dat"); + pcpp::Packet gtpPacket(&rawPacket1); + + PTF_ASSERT_TRUE(gtpPacket.isPacketOfType(pcpp::GTPv2)); + PTF_ASSERT_TRUE(gtpPacket.isPacketOfType(pcpp::GTP)); + auto gtpLayer = gtpPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(gtpLayer); + + PTF_ASSERT_EQUAL(gtpLayer->getMessageType(), pcpp::GtpV2MessageType::EchoRequest); + PTF_ASSERT_TRUE(gtpLayer->isPiggybacking()); + PTF_ASSERT_FALSE(gtpLayer->getTeid().first); + PTF_ASSERT_EQUAL(gtpLayer->getSequenceNumber(), 12345); + PTF_ASSERT_EQUAL(gtpLayer->toString(), "GTPv2 Layer, Echo Request message"); + PTF_ASSERT_EQUAL(gtpLayer->getInformationElementCount(), 1); + + gtpLayer = reinterpret_cast(gtpLayer->getNextLayer()); + PTF_ASSERT_NOT_NULL(gtpLayer); + + PTF_ASSERT_EQUAL(gtpLayer->getMessageType(), pcpp::GtpV2MessageType::CreateSessionResponse); + PTF_ASSERT_FALSE(gtpLayer->isPiggybacking()); + auto teid = gtpLayer->getTeid(); + PTF_ASSERT_TRUE(teid.first); + PTF_ASSERT_EQUAL(teid.second, 87654); + PTF_ASSERT_EQUAL(gtpLayer->getSequenceNumber(), 67890); + auto messagePriority = gtpLayer->getMessagePriority(); + PTF_ASSERT_TRUE(messagePriority.first); + PTF_ASSERT_EQUAL(messagePriority.second, 9); + PTF_ASSERT_EQUAL(gtpLayer->toString(), "GTPv2 Layer, Create Session Response message"); + PTF_ASSERT_NULL(gtpLayer->getNextLayer()); + } + + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gtpv2-with-piggyback-malformed.dat"); + pcpp::Packet gtpPacket(&rawPacket1); + PTF_ASSERT_TRUE(gtpPacket.isPacketOfType(pcpp::GTPv2)); + PTF_ASSERT_TRUE(gtpPacket.isPacketOfType(pcpp::GTP)); + auto gtpLayer = gtpPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(gtpLayer); + PTF_ASSERT_TRUE(gtpLayer->isPiggybacking()); + PTF_ASSERT_NOT_NULL(gtpLayer->getNextLayer()); + PTF_ASSERT_EQUAL(gtpLayer->getNextLayer()->getProtocol(), pcpp::GenericPayload); + } + + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gtpv2-over-tcp.dat"); + pcpp::Packet gtpPacket(&rawPacket1); + + PTF_ASSERT_TRUE(gtpPacket.isPacketOfType(pcpp::GTPv2)); + PTF_ASSERT_TRUE(gtpPacket.isPacketOfType(pcpp::GTP)); + auto gtpLayer = gtpPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(gtpLayer); + PTF_ASSERT_EQUAL(gtpLayer->getMessageType(), pcpp::GtpV2MessageType::DeleteSessionResponse); + PTF_ASSERT_EQUAL(gtpLayer->toString(), "GTPv2 Layer, Delete Session Response message"); + } + + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gtpv2-non-zero-cf-flag-instance.dat"); + pcpp::Packet gtpPacket(&rawPacket1); + + auto gtpLayer = gtpPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(gtpLayer); + auto infoElement = gtpLayer->getFirstInformationElement(); + PTF_ASSERT_EQUAL(infoElement.getCRFlag(), 7); + PTF_ASSERT_EQUAL(infoElement.getInstance(), 12); + } +} // GtpV2LayerParsingTest + +PTF_TEST_CASE(GtpV2LayerCreationTest) +{ + timeval time{}; + gettimeofday(&time, nullptr); + + { + pcpp::GtpV2Layer gtpLayer(pcpp::GtpV2MessageType::ModifyBearerRequest, 0x1a4a43, true, 0xd37d1590); + + // clang-format off + std::vector infoElementBuilders = { + { pcpp::GtpV2InformationElement::Type::Uli, 0, 0, { 0x18, 0x64, 0xf6, 0x29, 0x2e, 0x18, 0x64, 0xf6, 0x29, 0x01, 0xce, 0x66, 0x21 } }, + { pcpp::GtpV2InformationElement::Type::RatType, 0, 0, { 0x06 } }, + { pcpp::GtpV2InformationElement::Type::FTeid, 0, 0, { 0x86, 0xa4, 0x3e, 0xd0, 0x30, 0x6f, 0x47, 0xec, 0x31 } }, + { pcpp::GtpV2InformationElement::Type::Ambr, 0, 0, { 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00 } }, + { pcpp::GtpV2InformationElement::Type::Mei, 0, 0, { 0x53, 0x02, 0x89, 0x70, 0x72, 0x61, 0x23, 0x60 } }, + { pcpp::GtpV2InformationElement::Type::UeTimeZone, 0, 0, { 0x23, 0x00 } }, + { pcpp::GtpV2InformationElement::Type::BearerContext, 0, 0, { 0x49, 0x00, 0x01, 0x00, 0x05, 0x57, 0x00, 0x09, 0x01, 0x84, 0xa4, 0x30, 0xf3, 0xe2, 0x6f, 0x47, 0xec, 0x43 } }, + }; + // clang-format on + + for (const auto& infoElementBuilder : infoElementBuilders) + { + gtpLayer.addInformationElement(infoElementBuilder); + } + + // clang-format off + gtpLayer.addInformationElementAfter({ pcpp::GtpV2InformationElement::Type::ServingNetwork, 0, 0, { 0x64, 0xf6, 0x29 } }, + pcpp::GtpV2InformationElement::Type::Uli); + // clang-format on + gtpLayer.addInformationElementAfter({ pcpp::GtpV2InformationElement::Type::Recovery, 0, 0, { 0x12 } }, + pcpp::GtpV2InformationElement::Type::BearerContext); + + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gtpv2-with-teid.dat"); + pcpp::Packet gtpPacket1(&rawPacket1); + + auto expectedGtpLayer = gtpPacket1.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedGtpLayer); + + PTF_ASSERT_EQUAL(gtpLayer.getDataLen(), expectedGtpLayer->getDataLen()); + PTF_ASSERT_BUF_COMPARE(gtpLayer.getData(), expectedGtpLayer->getData(), gtpLayer.getDataLen()); + } + + { + pcpp::EthLayer ethLayer("10:5b:ad:b0:f5:07", "08:b4:b1:1a:46:ad", PCPP_ETHERTYPE_IP); + pcpp::IPv4Layer ipLayer(pcpp::IPv4Address("192.168.1.100"), pcpp::IPv4Address("192.168.1.200")); + ipLayer.getIPv4Header()->ipId = htobe16(1); + ipLayer.getIPv4Header()->timeToLive = 64; + pcpp::UdpLayer udpLayer(2123, 2123); + + pcpp::GtpV2Layer gtpLayer(pcpp::GtpV2MessageType::EchoRequest, 0x003039); + gtpLayer.addInformationElement({ pcpp::GtpV2InformationElement::Type::Recovery, 0, 0, { 0x11 } }); + + pcpp::GtpV2Layer piggybackGtpLayer(pcpp::GtpV2MessageType::CreateSessionResponse, 0x010932, true, 0x00015666, + true, 9); + piggybackGtpLayer.addInformationElement({ + pcpp::GtpV2InformationElement::Type::Imsi, 0, 0, { 0x33, 0x87, 0x93, 0x34, 0x49, 0x51, 0x83, 0xf6 } + }); + + pcpp::Packet newPacket; + newPacket.addLayer(ðLayer); + newPacket.addLayer(&ipLayer); + newPacket.addLayer(&udpLayer); + newPacket.addLayer(>pLayer); + newPacket.addLayer(&piggybackGtpLayer); + newPacket.computeCalculateFields(); + + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gtpv2-with-piggyback.dat"); + pcpp::Packet expectedPacket(&rawPacket1); + + PTF_ASSERT_EQUAL(newPacket.getRawPacket()->getRawDataLen(), expectedPacket.getRawPacket()->getRawDataLen()); + PTF_ASSERT_BUF_COMPARE(newPacket.getRawPacket()->getRawData(), expectedPacket.getRawPacket()->getRawData(), + newPacket.getRawPacket()->getRawDataLen()); + } + + { + pcpp::GtpV2Layer gtpLayer(pcpp::GtpV2MessageType::ModifyBearerRequest, 0x1a4a43, true, 0xd37d1590, true, 1); + gtpLayer.addInformationElement({ + pcpp::GtpV2InformationElement::Type::Uli, + 7, + 12, + { 0x18, 0x64, 0xf6, 0x29, 0x2e, 0x18, 0x64, 0xf6, 0x29, 0x01, 0xce, 0x66, 0x21 } + }); + + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gtpv2-non-zero-cf-flag-instance.dat"); + pcpp::Packet gtpPacket(&rawPacket1); + + auto expectedGtpLayer = gtpPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedGtpLayer); + + PTF_ASSERT_EQUAL(gtpLayer.getDataLen(), expectedGtpLayer->getDataLen()); + PTF_ASSERT_BUF_COMPARE(gtpLayer.getData(), expectedGtpLayer->getData(), expectedGtpLayer->getDataLen()); + } +} // GtpV2LayerCreationTest + +PTF_TEST_CASE(GtpV2LayerEditTest) +{ + timeval time{}; + gettimeofday(&time, nullptr); + + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gtpv2-non-zero-cf-flag-instance.dat"); + pcpp::Packet gtpPacket1(&rawPacket1); + + auto expectedGtpLayer1 = gtpPacket1.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedGtpLayer1); + + { + pcpp::GtpV2Layer gtpLayer(pcpp::GtpV2MessageType::ISRStatus, 0x10); + gtpLayer.addInformationElement({ + pcpp::GtpV2InformationElement::Type::Uli, + 7, + 12, + { 0x18, 0x64, 0xf6, 0x29, 0x2e, 0x18, 0x64, 0xf6, 0x29, 0x01, 0xce, 0x66, 0x21 } + }); + + gtpLayer.setMessageType(pcpp::GtpV2MessageType::ModifyBearerRequest); + gtpLayer.setTeid(0xd37d1590); + gtpLayer.setMessagePriority(1); + gtpLayer.setSequenceNumber(0x1a4a43); + + PTF_ASSERT_EQUAL(gtpLayer.getDataLen(), expectedGtpLayer1->getDataLen()); + PTF_ASSERT_BUF_COMPARE(gtpLayer.getData(), expectedGtpLayer1->getData(), expectedGtpLayer1->getDataLen()); + } + + { + pcpp::GtpV2Layer gtpLayer(pcpp::GtpV2MessageType::ModifyBearerRequest, 0x1a4a43, true, 1, true, 2); + gtpLayer.addInformationElement({ + pcpp::GtpV2InformationElement::Type::Uli, + 7, + 12, + { 0x18, 0x64, 0xf6, 0x29, 0x2e, 0x18, 0x64, 0xf6, 0x29, 0x01, 0xce, 0x66, 0x21 } + }); + + gtpLayer.setTeid(0xd37d1590); + gtpLayer.setMessagePriority(1); + + PTF_ASSERT_EQUAL(gtpLayer.getDataLen(), expectedGtpLayer1->getDataLen()); + PTF_ASSERT_BUF_COMPARE(gtpLayer.getData(), expectedGtpLayer1->getData(), expectedGtpLayer1->getDataLen()); + } + + { + pcpp::GtpV2Layer gtpLayer(pcpp::GtpV2MessageType::EchoRequest, 12345, true, 1, true, 2); + gtpLayer.addInformationElement({ pcpp::GtpV2InformationElement::Type::Recovery, 0, 0, { 0x11 } }); + + READ_FILE_AND_CREATE_PACKET(2, "PacketExamples/gtpv2-with-piggyback.dat"); + pcpp::Packet gtpPacket2(&rawPacket2); + + auto expectedGtpLayer2 = gtpPacket2.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedGtpLayer2); + + gtpLayer.unsetMessagePriority(); + gtpLayer.unsetTeid(); + + PTF_ASSERT_EQUAL(gtpLayer.getHeaderLen(), expectedGtpLayer2->getHeaderLen()); + PTF_ASSERT_EQUAL(gtpLayer.getData()[0], 0x40); + PTF_ASSERT_BUF_COMPARE(gtpLayer.getData() + 1, expectedGtpLayer2->getData() + 1, + expectedGtpLayer2->getHeaderLen() - 1); + + gtpLayer.unsetMessagePriority(); + gtpLayer.unsetTeid(); + + PTF_ASSERT_EQUAL(gtpLayer.getHeaderLen(), expectedGtpLayer2->getHeaderLen()); + PTF_ASSERT_EQUAL(gtpLayer.getData()[0], 0x40); + PTF_ASSERT_BUF_COMPARE(gtpLayer.getData() + 1, expectedGtpLayer2->getData() + 1, + expectedGtpLayer2->getHeaderLen() - 1); + } + + { + pcpp::GtpV2Layer gtpLayer(pcpp::GtpV2MessageType::ModifyBearerRequest, 0x1a4a43, true, 0xd37d1590, true, 1); + gtpLayer.addInformationElement({ + pcpp::GtpV2InformationElement::Type::Imsi, 0, 0, { 0x33, 0x87, 0x93, 0x34, 0x49, 0x51, 0x83, 0xf6 } + }); + gtpLayer.addInformationElement({ + pcpp::GtpV2InformationElement::Type::Uli, + 7, + 12, + { 0x18, 0x64, 0xf6, 0x29, 0x2e, 0x18, 0x64, 0xf6, 0x29, 0x01, 0xce, 0x66, 0x21 } + }); + gtpLayer.addInformationElement({ + pcpp::GtpV2InformationElement::Type::Imsi, 0, 0, { 0x33, 0x87, 0x93, 0x34, 0x49, 0x51, 0x83, 0xf6 } + }); + gtpLayer.addInformationElement({ pcpp::GtpV2InformationElement::Type::Recovery, 0, 0, { 0x11 } }); + + PTF_ASSERT_TRUE(gtpLayer.removeInformationElement(pcpp::GtpV2InformationElement::Type::Recovery)); + PTF_ASSERT_TRUE(gtpLayer.removeInformationElement(pcpp::GtpV2InformationElement::Type::Imsi)); + PTF_ASSERT_TRUE(gtpLayer.removeInformationElement(pcpp::GtpV2InformationElement::Type::Imsi)); + PTF_ASSERT_FALSE(gtpLayer.removeInformationElement(pcpp::GtpV2InformationElement::Type::Imsi)); + + PTF_ASSERT_EQUAL(gtpLayer.getInformationElementCount(), 1); + + PTF_ASSERT_EQUAL(gtpLayer.getDataLen(), expectedGtpLayer1->getDataLen()); + PTF_ASSERT_BUF_COMPARE(gtpLayer.getData(), expectedGtpLayer1->getData(), expectedGtpLayer1->getDataLen()); + } + + { + pcpp::GtpV2Layer gtpLayer(pcpp::GtpV2MessageType::ModifyBearerRequest, 0x1a4a43, true, 0xd37d1590, true, 1); + gtpLayer.addInformationElement({ + pcpp::GtpV2InformationElement::Type::Imsi, 0, 0, { 0x33, 0x87, 0x93, 0x34, 0x49, 0x51, 0x83, 0xf6 } + }); + gtpLayer.addInformationElement({ + pcpp::GtpV2InformationElement::Type::Uli, + 7, + 12, + { 0x18, 0x64, 0xf6, 0x29, 0x2e, 0x18, 0x64, 0xf6, 0x29, 0x01, 0xce, 0x66, 0x21 } + }); + gtpLayer.addInformationElement({ pcpp::GtpV2InformationElement::Type::Recovery, 0, 0, { 0x11 } }); + + gtpLayer.removeAllInformationElements(); + + PTF_ASSERT_EQUAL(gtpLayer.getInformationElementCount(), 0); + + READ_FILE_AND_CREATE_PACKET(2, "PacketExamples/gtpv2-no-info-elements.dat"); + pcpp::Packet gtpPacket2(&rawPacket2); + + auto expectedGtpLayer2 = gtpPacket2.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedGtpLayer2); + + PTF_ASSERT_EQUAL(gtpLayer.getDataLen(), expectedGtpLayer2->getDataLen()); + PTF_ASSERT_BUF_COMPARE(gtpLayer.getData(), expectedGtpLayer2->getData(), expectedGtpLayer2->getDataLen()); + } +} // GtpV2LayerEditTest diff --git a/Tests/Packet++Test/main.cpp b/Tests/Packet++Test/main.cpp index e74498bd9..416378011 100644 --- a/Tests/Packet++Test/main.cpp +++ b/Tests/Packet++Test/main.cpp @@ -249,9 +249,12 @@ int main(int argc, char* argv[]) PTF_RUN_TEST(RadiusLayerCreationTest, "radius"); PTF_RUN_TEST(RadiusLayerEditTest, "radius"); - PTF_RUN_TEST(GtpLayerParsingTest, "gtp"); - PTF_RUN_TEST(GtpLayerCreationTest, "gtp"); - PTF_RUN_TEST(GtpLayerEditTest, "gtp"); + PTF_RUN_TEST(GtpV1LayerParsingTest, "gtp"); + PTF_RUN_TEST(GtpV1LayerCreationTest, "gtp"); + PTF_RUN_TEST(GtpV1LayerEditTest, "gtp"); + PTF_RUN_TEST(GtpV2LayerParsingTest, "gtp"); + PTF_RUN_TEST(GtpV2LayerCreationTest, "gtp"); + PTF_RUN_TEST(GtpV2LayerEditTest, "gtp"); PTF_RUN_TEST(BgpLayerParsingTest, "bgp"); PTF_RUN_TEST(BgpLayerCreationTest, "bgp"); diff --git a/translation/README-kor.md b/translation/README-kor.md index 8bdeddddb..92cebd578 100644 --- a/translation/README-kor.md +++ b/translation/README-kor.md @@ -225,7 +225,7 @@ PcapPlusPlus는 현재 다음 프로토콜의 패킷을 파싱, 편집 및 생 ### 전송 계층 (L4) 26. COTP -27. GTP (v1) +27. GTP (v1 & v2) 28. IPSec AH & ESP - 파싱만 가능 (편집 불가) 29. TCP 30. TPKT diff --git a/translation/README-zh-tw.md b/translation/README-zh-tw.md index a0eb600cc..ba49a92f0 100644 --- a/translation/README-zh-tw.md +++ b/translation/README-zh-tw.md @@ -248,7 +248,7 @@ PcapPlusPlus 目前支援解析、編輯和建構以下網路協定的封包: ### 傳輸層 (L4) 26. COTP -27. GTP (v1) +27. GTP (v1 & v2) 28. IPSec AH 和 ESP - 僅支援解析(不支援編輯) 29. TCP 30. TPKT